{
  "openapi": "3.0.3",
  "info": {
    "title": "Integration Toolkit API",
    "version": "1.33.0",
    "description": "API for integrating with external systems in a standardised way."
  },
  "tags": [
    {
      "name": "erp",
      "description": "ERP integration endpoints"
    },
    {
      "name": "erp-imports",
      "description": "ERP pricing file import endpoints"
    },
    {
      "name": "trigger",
      "description": "Endpoints to trigger ERP related actions"
    },
    {
      "name": "integrations",
      "description": "Integration and Use Case management endpoints"
    },
    {
      "name": "monitoring",
      "description": "Monitoring and analytics endpoints"
    },
    {
      "name": "managed-call",
      "description": "Managed call endpoints for synchronous external API calls"
    },
    {
      "name": "proxy",
      "description": "Secure proxy endpoints"
    }
  ],
  "servers": [
    {
      "url": "https://integration-toolkit.sls.epilot.io"
    }
  ],
  "security": [
    {
      "EpilotAuth": []
    }
  ],
  "paths": {
    "/v1/erp/tracking/acknowledgement": {
      "post": {
        "operationId": "acknowledgeTracking",
        "summary": "acknowledgeTracking",
        "description": "Acknowledges an ERP tracking record by removing it from the tracking table, requires public authentication",
        "tags": [
          "erp"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "ack_id"
                ],
                "properties": {
                  "ack_id": {
                    "type": "string",
                    "description": "Unique identifier of the ERP tracking record to acknowledge"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledgment successful",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request - missing ack_id"
          },
          "401": {
            "description": "Unauthorized - invalid or missing token"
          },
          "404": {
            "description": "Record not found"
          },
          "500": {
            "description": "Server error processing the request"
          }
        }
      }
    },
    "/v1/erp/trigger": {
      "post": {
        "operationId": "triggerErp",
        "summary": "triggerErp",
        "description": "Triggers the ERP integration process",
        "tags": [
          "erp",
          "trigger"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TriggerErpActionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/TriggerWebhookResponse"
          },
          "400": {
            "description": "Bad request - invalid or missing parameters"
          },
          "401": {
            "description": "Unauthorized - invalid or missing token"
          },
          "500": {
            "description": "Server error processing the request"
          }
        }
      }
    },
    "/v1/erp/updates/events": {
      "post": {
        "deprecated": true,
        "operationId": "processErpUpdatesEvents",
        "summary": "processErpUpdatesEvents",
        "description": "Handles updates from ERP systems and tracks them appropriately",
        "tags": [
          "erp"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "app_id",
                  "component_id",
                  "meta",
                  "events"
                ],
                "properties": {
                  "app_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "UUID that identifies the specific integration app instance"
                  },
                  "component_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "UUID that identifies the specific integration app component instance"
                  },
                  "meta": {
                    "type": "object",
                    "description": "Metadata to be passed along with the events",
                    "additionalProperties": true,
                    "properties": {
                      "correlation_id": {
                        "type": "string",
                        "description": "ID that identifies the specific request for debugging purposes"
                      }
                    }
                  },
                  "events": {
                    "type": "array",
                    "description": "List of ERP events to process",
                    "items": {
                      "$ref": "#/components/schemas/ErpEvent"
                    }
                  }
                }
              },
              "example": {
                "app_id": "123e4567-e89b-12d3-a456-426614174000",
                "component_id": "123e4567-e89b-12d3-a456-426614174000",
                "meta": {
                  "correlation_id": "req-789e4567-e89b-12d3-a456-426614174000"
                },
                "events": [
                  {
                    "event_type": "CREATE",
                    "object_type": "business_partner",
                    "timestamp": "2025-05-01T08:30:00Z",
                    "format": "json",
                    "payload": "{\"id\":\"BP10001\",\"name\":\"Acme Corporation\",\"type\":\"organization\",\"tax_id\":\"DE123456789\",\"status\":\"active\"}",
                    "deduplication_id": "bp-create-20250501-083000-001"
                  },
                  {
                    "event_type": "UPDATE",
                    "object_type": "contract_account",
                    "timestamp": "2025-05-01T08:35:00Z",
                    "format": "json",
                    "payload": "{\"id\":\"CA20001\",\"business_partner_id\":\"BP10001\",\"status\":\"active\",\"payment_method\":\"direct_debit\"}",
                    "deduplication_id": "ca-update-20250501-083500-001"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/ERPUpdatesResponse"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/erp/updates/events": {
      "post": {
        "deprecated": true,
        "operationId": "processErpUpdatesEventsV2",
        "summary": "processErpUpdatesEventsV2",
        "description": "Handles updates from ERP systems using integration_id directly.\nThis is the v2 version that simplifies the API by accepting integration_id\ninstead of app_id and component_id.\n",
        "tags": [
          "erp"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ErpUpdatesEventsV2Request"
              },
              "example": {
                "integration_id": "123e4567-e89b-12d3-a456-426614174000",
                "correlation_id": "req-789e4567-e89b-12d3-a456-426614174000",
                "events": [
                  {
                    "event_type": "CREATE",
                    "object_type": "business_partner",
                    "timestamp": "2025-05-01T08:30:00Z",
                    "format": "json",
                    "payload": "{\"id\":\"BP10001\",\"name\":\"Acme Corporation\",\"type\":\"organization\",\"tax_id\":\"DE123456789\",\"status\":\"active\"}",
                    "deduplication_id": "bp-create-20250501-083000-001"
                  },
                  {
                    "event_type": "UPDATE",
                    "object_type": "contract_account",
                    "timestamp": "2025-05-01T08:35:00Z",
                    "format": "json",
                    "payload": "{\"id\":\"CA20001\",\"business_partner_id\":\"BP10001\",\"status\":\"active\",\"payment_method\":\"direct_debit\"}",
                    "deduplication_id": "ca-update-20250501-083500-001"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/ERPUpdatesResponse"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v3/erp/updates/events": {
      "post": {
        "operationId": "processErpUpdatesEventsV3",
        "summary": "processErpUpdatesEventsV3",
        "description": "Handles updates from ERP systems using integration_id directly.\nThis is the v3 version that removes the unused event_type field and renames object_type to event_name\nto align with the integration UI naming.\n",
        "tags": [
          "erp"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ErpUpdatesEventsV3Request"
              },
              "example": {
                "integration_id": "123e4567-e89b-12d3-a456-426614174000",
                "correlation_id": "req-789e4567-e89b-12d3-a456-426614174000",
                "events": [
                  {
                    "event_name": "business_partner",
                    "timestamp": "2025-05-01T08:30:00Z",
                    "format": "json",
                    "payload": "{\"id\":\"BP10001\",\"name\":\"Acme Corporation\",\"type\":\"organization\",\"tax_id\":\"DE123456789\",\"status\":\"active\"}",
                    "deduplication_id": "bp-create-20250501-083000-001"
                  },
                  {
                    "event_name": "contract_account",
                    "timestamp": "2025-05-01T08:35:00Z",
                    "format": "json",
                    "payload": "{\"id\":\"CA20001\",\"business_partner_id\":\"BP10001\",\"status\":\"active\",\"payment_method\":\"direct_debit\"}",
                    "deduplication_id": "ca-update-20250501-083500-001"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/ERPUpdatesResponse"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/erp/updates/mapping_simulation": {
      "post": {
        "operationId": "simulateMappingV2",
        "summary": "simulateMappingV2",
        "description": "Test v2.0 mapping configuration by transforming a payload using the provided mapping rules without persisting data.\n\nThis endpoint accepts the same configuration format that is stored in the integration use case resource,\nmaking it easier to test configurations before saving them to a use case.\n\nSee documentation at /docs/MAPPING_V2.md for detailed v2.0 format specification.\n",
        "tags": [
          "erp"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MappingSimulationV2Request"
              },
              "examples": {
                "simple": {
                  "summary": "Simple Example",
                  "value": {
                    "event_configuration": {
                      "entities": [
                        {
                          "entity_schema": "contact",
                          "unique_ids": [
                            "customer_number"
                          ],
                          "fields": [
                            {
                              "attribute": "customer_number",
                              "field": "customerId"
                            },
                            {
                              "attribute": "first_name",
                              "field": "firstName"
                            },
                            {
                              "attribute": "last_name",
                              "field": "lastName"
                            },
                            {
                              "attribute": "email",
                              "field": "email"
                            },
                            {
                              "attribute": "source",
                              "constant": "ERP"
                            }
                          ]
                        }
                      ]
                    },
                    "format": "json",
                    "payload": "{\"customerId\":\"12345\",\"firstName\":\"Anna\",\"lastName\":\"Schmidt\",\"email\":\"anna@example.com\"}"
                  }
                },
                "advanced_with_relations": {
                  "summary": "Advanced with Relations",
                  "value": {
                    "event_configuration": {
                      "entities": [
                        {
                          "entity_schema": "contract",
                          "unique_ids": [
                            "contract_number"
                          ],
                          "fields": [
                            {
                              "attribute": "contract_number",
                              "field": "number"
                            },
                            {
                              "attribute": "display_name",
                              "field": "displayName"
                            },
                            {
                              "attribute": "billing_account",
                              "relations": {
                                "operation": "_set",
                                "items": [
                                  {
                                    "entity_schema": "billing_account",
                                    "_tags": [
                                      "PRIMARY"
                                    ],
                                    "unique_ids": [
                                      {
                                        "attribute": "external_id",
                                        "field": "customerAccountId"
                                      }
                                    ]
                                  }
                                ]
                              }
                            }
                          ]
                        },
                        {
                          "entity_schema": "meter",
                          "unique_ids": [
                            "meter_number"
                          ],
                          "jsonataExpression": "meters.{\"number\": number, \"type\": meterType}",
                          "fields": [
                            {
                              "attribute": "meter_number",
                              "field": "number"
                            },
                            {
                              "attribute": "meter_type",
                              "field": "type"
                            }
                          ]
                        }
                      ]
                    },
                    "format": "json",
                    "payload": "{\"number\":\"CTR-001\",\"displayName\":\"Premium Contract\",\"customerAccountId\":\"ACC-999\",\"meters\":[{\"number\":\"M-001\",\"meterType\":\"electricity\"},{\"number\":\"M-002\",\"meterType\":\"gas\"}]}"
                  }
                },
                "with_meter_readings": {
                  "summary": "With Meter Readings",
                  "value": {
                    "event_configuration": {
                      "entities": [
                        {
                          "entity_schema": "contract",
                          "unique_ids": [
                            "contract_number"
                          ],
                          "fields": [
                            {
                              "attribute": "contract_number",
                              "field": "number"
                            },
                            {
                              "attribute": "display_name",
                              "field": "name"
                            }
                          ]
                        }
                      ],
                      "meter_readings": [
                        {
                          "jsonataExpression": "$.readings",
                          "meter": {
                            "unique_ids": [
                              {
                                "attribute": "external_id",
                                "field": "meter_id"
                              }
                            ]
                          },
                          "fields": [
                            {
                              "attribute": "external_id",
                              "field": "reading_id"
                            },
                            {
                              "attribute": "timestamp",
                              "field": "read_at"
                            },
                            {
                              "attribute": "source",
                              "constant": "ERP"
                            },
                            {
                              "attribute": "value",
                              "field": "value"
                            }
                          ]
                        }
                      ]
                    },
                    "format": "json",
                    "payload": "{\"number\":\"CTR-001\",\"name\":\"Main Contract\",\"readings\":[{\"meter_id\":\"M-001\",\"reading_id\":\"R-001\",\"read_at\":\"2025-01-15T10:00:00Z\",\"value\":12345.6}]}"
                  }
                },
                "conditional_fields": {
                  "summary": "Conditional Field Processing",
                  "description": "Example showing how to conditionally enable/disable individual field mappings",
                  "value": {
                    "event_configuration": {
                      "entities": [
                        {
                          "entity_schema": "contact",
                          "unique_ids": [
                            "customer_number"
                          ],
                          "fields": [
                            {
                              "attribute": "customer_number",
                              "field": "customerId"
                            },
                            {
                              "attribute": "email",
                              "field": "email",
                              "enabled": "$exists(email) and email != ''"
                            },
                            {
                              "attribute": "phone",
                              "field": "phone",
                              "enabled": "$exists(phone) and phone != ''"
                            },
                            {
                              "attribute": "vip_status",
                              "constant": "VIP",
                              "enabled": "vipFlag = true"
                            },
                            {
                              "attribute": "billing_account",
                              "enabled": "$exists(billingAccountId)",
                              "relations": {
                                "operation": "_set",
                                "items": [
                                  {
                                    "entity_schema": "billing_account",
                                    "unique_ids": [
                                      {
                                        "attribute": "external_id",
                                        "field": "billingAccountId"
                                      }
                                    ]
                                  }
                                ]
                              }
                            }
                          ]
                        }
                      ]
                    },
                    "format": "json",
                    "payload": "{\"customerId\":\"12345\",\"email\":\"user@example.com\",\"phone\":\"\",\"vipFlag\":true,\"billingAccountId\":\"ACC-999\"}"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully simulated mapping",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MappingSimulationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "description": "Unprocessable entity - mapping configuration or payload validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseBase"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/erp/updates/direct_simulation": {
      "post": {
        "operationId": "simulateDirect",
        "summary": "simulateDirect",
        "description": "Dry run for direct-mode payloads: validates a `DirectPayload` against a `direct: true`\nuse case configuration and previews the internal entity and meter reading updates the\npipeline would apply — without persisting anything.\n\nUnlike the ingest endpoint, validation does not stop at the first problem: ALL issues\nacross all operations are collected into `errors`, each carrying the index of the\noperation it refers to. When the payload is valid, the response additionally contains\nthe translated update previews plus non-blocking `warnings` (for example unique-id\nfields that are not defined in the target entity schema, or a failed server-side\nunique-id type derivation).\n\nUse this endpoint while designing a direct integration, before sending events to\n`POST /v3/erp/updates/events`. See documentation at /docs/DIRECT_MODE.md.\n",
        "tags": [
          "erp"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DirectSimulationRequest"
              },
              "examples": {
                "entity_upsert": {
                  "summary": "Entity upsert (open mode)",
                  "value": {
                    "event_configuration": {
                      "direct": true
                    },
                    "payload": {
                      "version": "1",
                      "operations": [
                        {
                          "entity_slug": "contact",
                          "unique_ids": {
                            "customer_number": "CUST-12345"
                          },
                          "attributes": {
                            "first_name": "Anna",
                            "last_name": "Schmidt",
                            "email": "anna.schmidt@example.com"
                          }
                        }
                      ]
                    }
                  }
                },
                "allowlist_with_relation": {
                  "summary": "Allowlisted entities with a relation",
                  "value": {
                    "event_configuration": {
                      "direct": true,
                      "entities": [
                        {
                          "entity_schema": "contract",
                          "unique_ids": [
                            "contract_number"
                          ]
                        },
                        {
                          "entity_schema": "contact",
                          "unique_ids": [
                            "customer_number"
                          ]
                        }
                      ]
                    },
                    "payload": {
                      "version": "1",
                      "operations": [
                        {
                          "entity_slug": "contract",
                          "unique_ids": {
                            "contract_number": "CTR-001"
                          },
                          "attributes": {
                            "display_name": "Premium Contract",
                            "customer": {
                              "$relation": {
                                "_set": [
                                  {
                                    "schema": "contact",
                                    "unique_ids": {
                                      "customer_number": "CUST-12345"
                                    },
                                    "tags": [
                                      "primary"
                                    ]
                                  }
                                ]
                              }
                            }
                          }
                        }
                      ]
                    }
                  }
                },
                "meter_reading": {
                  "summary": "Meter reading",
                  "value": {
                    "event_configuration": {
                      "direct": true
                    },
                    "payload": {
                      "version": "1",
                      "operations": [
                        {
                          "type": "meter_reading",
                          "meter": {
                            "unique_ids": {
                              "meter_number": "M-001"
                            }
                          },
                          "counter": {
                            "unique_ids": {
                              "obis_number": "1-0:1.8.0"
                            }
                          },
                          "reading_matching": "strict-date",
                          "attributes": {
                            "external_id": "R-001",
                            "timestamp": "2026-08-24T06:00:00Z",
                            "source": "ERP",
                            "value": 12345.6
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Simulation executed - `valid` reports the verdict, `errors` lists every violation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DirectSimulationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "description": "Unprocessable entity - simulation could not be executed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseBase"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/erp/updates/mapping_simulation": {
      "post": {
        "operationId": "simulateMapping",
        "summary": "simulateMapping",
        "description": "Test mapping configuration by transforming a payload using the provided mapping rules without persisting data.\n\nSupports both v1.0 (object-based) and v2.0 (event-based) mapping formats.\nSee documentation at /docs/MAPPING_V2.md for detailed v2.0 format specification.\n",
        "tags": [
          "erp"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MappingSimulationRequest"
              },
              "examples": {
                "v1_example": {
                  "summary": "Version 1.0 Example",
                  "value": {
                    "mapping_configuration": {
                      "version": "1.0",
                      "mapping": {
                        "objects": {
                          "business_partner": {
                            "unique_ids": {
                              "contact": {
                                "partner_id": "_id"
                              }
                            },
                            "fields": [
                              {
                                "entity": "contact",
                                "attribute": "first_name",
                                "field": "firstName"
                              },
                              {
                                "entity": "contact",
                                "attribute": "last_name",
                                "field": "lastName"
                              }
                            ]
                          }
                        }
                      }
                    },
                    "object_type": "business_partner",
                    "format": "json",
                    "payload": "{\"partner_id\":\"BP10001\",\"firstName\":\"John\",\"lastName\":\"Doe\"}"
                  }
                },
                "v2_simple": {
                  "summary": "Version 2.0 - Simple Example",
                  "value": {
                    "mapping_configuration": {
                      "version": "2.0",
                      "mapping": {
                        "events": {
                          "CustomerChanged": {
                            "entities": [
                              {
                                "entity_schema": "contact",
                                "unique_ids": [
                                  "customer_number"
                                ],
                                "fields": [
                                  {
                                    "attribute": "customer_number",
                                    "field": "customerId"
                                  },
                                  {
                                    "attribute": "first_name",
                                    "field": "firstName"
                                  },
                                  {
                                    "attribute": "last_name",
                                    "field": "lastName"
                                  },
                                  {
                                    "attribute": "email",
                                    "field": "email"
                                  },
                                  {
                                    "attribute": "source",
                                    "constant": "ERP"
                                  }
                                ]
                              }
                            ]
                          }
                        }
                      }
                    },
                    "object_type": "CustomerChanged",
                    "format": "json",
                    "payload": "{\"customerId\":\"12345\",\"firstName\":\"Anna\",\"lastName\":\"Schmidt\",\"email\":\"anna@example.com\"}"
                  }
                },
                "v2_advanced": {
                  "summary": "Version 2.0 - Advanced with Relations",
                  "value": {
                    "mapping_configuration": {
                      "version": "2.0",
                      "mapping": {
                        "events": {
                          "ContractChanged": {
                            "entities": [
                              {
                                "entity_schema": "contract",
                                "unique_ids": [
                                  "contract_number"
                                ],
                                "fields": [
                                  {
                                    "attribute": "contract_number",
                                    "field": "number"
                                  },
                                  {
                                    "attribute": "display_name",
                                    "field": "displayName"
                                  },
                                  {
                                    "attribute": "billing_account",
                                    "relations": {
                                      "operation": "_set",
                                      "items": [
                                        {
                                          "entity_schema": "billing_account",
                                          "_tags": [
                                            "PRIMARY"
                                          ],
                                          "unique_ids": [
                                            {
                                              "attribute": "external_id",
                                              "field": "customerAccountId"
                                            }
                                          ]
                                        }
                                      ]
                                    }
                                  }
                                ]
                              },
                              {
                                "entity_schema": "meter",
                                "unique_ids": [
                                  "meter_number"
                                ],
                                "jsonataExpression": "meters.{\"number\": number, \"type\": meterType}",
                                "fields": [
                                  {
                                    "attribute": "meter_number",
                                    "field": "number"
                                  },
                                  {
                                    "attribute": "meter_type",
                                    "field": "type"
                                  }
                                ]
                              }
                            ]
                          }
                        }
                      }
                    },
                    "object_type": "ContractChanged",
                    "format": "json",
                    "payload": "{\"number\":\"CTR-001\",\"displayName\":\"Premium Contract\",\"customerAccountId\":\"ACC-999\",\"meters\":[{\"number\":\"M-001\",\"meterType\":\"electricity\"},{\"number\":\"M-002\",\"meterType\":\"gas\"}]}"
                  }
                },
                "v2_append_operation": {
                  "summary": "Version 2.0 - Using _append Operation",
                  "description": "Example showing how to use _append to add relations to existing entities",
                  "value": {
                    "mapping_configuration": {
                      "version": "2.0",
                      "mapping": {
                        "events": {
                          "ContactAdded": {
                            "entities": [
                              {
                                "entity_schema": "contract",
                                "unique_ids": [
                                  "contract_number"
                                ],
                                "fields": [
                                  {
                                    "attribute": "contract_number",
                                    "field": "contractNumber"
                                  },
                                  {
                                    "attribute": "additional_contacts",
                                    "relations": {
                                      "operation": "_append",
                                      "items": [
                                        {
                                          "entity_schema": "contact",
                                          "_tags": [
                                            "ADDITIONAL"
                                          ],
                                          "unique_ids": [
                                            {
                                              "attribute": "email",
                                              "field": "contactEmail"
                                            }
                                          ]
                                        }
                                      ]
                                    }
                                  }
                                ]
                              }
                            ]
                          }
                        }
                      }
                    },
                    "object_type": "ContactAdded",
                    "format": "json",
                    "payload": "{\"contractNumber\":\"CTR-001\",\"contactEmail\":\"new.contact@example.com\"}"
                  }
                },
                "v2_field_enabled": {
                  "summary": "Version 2.0 - Field-Level Conditional Processing",
                  "description": "Example showing how to conditionally enable/disable individual field mappings using the enabled property",
                  "value": {
                    "mapping_configuration": {
                      "version": "2.0",
                      "mapping": {
                        "events": {
                          "CustomerChanged": {
                            "entities": [
                              {
                                "entity_schema": "contact",
                                "unique_ids": [
                                  "customer_number"
                                ],
                                "fields": [
                                  {
                                    "attribute": "customer_number",
                                    "field": "customerId"
                                  },
                                  {
                                    "attribute": "email",
                                    "field": "email",
                                    "enabled": true
                                  },
                                  {
                                    "attribute": "phone",
                                    "field": "phone",
                                    "enabled": "$exists(phone) and phone != ''"
                                  },
                                  {
                                    "attribute": "mobile",
                                    "field": "mobile",
                                    "enabled": "$exists(mobile)"
                                  },
                                  {
                                    "attribute": "vip_status",
                                    "constant": "VIP",
                                    "enabled": "vipFlag = true"
                                  },
                                  {
                                    "attribute": "internal_notes",
                                    "field": "notes",
                                    "enabled": false
                                  },
                                  {
                                    "attribute": "billing_account",
                                    "enabled": "$exists(billingAccountId)",
                                    "relations": {
                                      "operation": "_set",
                                      "items": [
                                        {
                                          "entity_schema": "billing_account",
                                          "unique_ids": [
                                            {
                                              "attribute": "external_id",
                                              "field": "billingAccountId"
                                            }
                                          ]
                                        }
                                      ]
                                    }
                                  }
                                ]
                              }
                            ]
                          }
                        }
                      }
                    },
                    "object_type": "CustomerChanged",
                    "format": "json",
                    "payload": "{\"customerId\":\"12345\",\"email\":\"user@example.com\",\"phone\":\"\",\"mobile\":\"555-1234\",\"vipFlag\":true,\"notes\":\"Internal only\",\"billingAccountId\":\"ACC-999\"}"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successfully simulated mapping",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MappingSimulationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "description": "Unprocessable entity - mapping configuration or payload validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseBase"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations": {
      "get": {
        "operationId": "listIntegrations",
        "summary": "listIntegrations",
        "description": "Retrieve all integrations for the authenticated organization",
        "tags": [
          "integrations"
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved integrations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "integrations"
                  ],
                  "properties": {
                    "integrations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Integration"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "post": {
        "operationId": "createIntegration",
        "summary": "createIntegration",
        "description": "Create a new integration configuration",
        "tags": [
          "integrations"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateIntegrationRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Integration created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Integration"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}": {
      "get": {
        "operationId": "getIntegration",
        "summary": "getIntegration",
        "description": "Retrieve a specific integration by its ID",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved integration",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Integration"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Integration not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "put": {
        "operationId": "updateIntegration",
        "summary": "updateIntegration",
        "description": "Update an existing integration configuration",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateIntegrationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Integration updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Integration"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Integration not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteIntegration",
        "summary": "deleteIntegration",
        "description": "Delete an integration and all its use cases",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Integration deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Integration not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/events": {
      "post": {
        "operationId": "queryEvents",
        "summary": "queryEvents",
        "description": "Query events for a specific integration",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QueryEventsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueryEventsResponse"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/events/replay": {
      "post": {
        "operationId": "replayEvents",
        "summary": "replayEvents",
        "description": "Replay one or more events for a specific integration. Events will be re-processed with their original payloads but with a new correlation ID for traceability.",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReplayEventsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/ReplayEventsResponse"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/use-cases": {
      "get": {
        "operationId": "listUseCases",
        "summary": "listUseCases",
        "description": "Retrieve all use cases for a specific integration",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved use cases",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "use_cases"
                  ],
                  "properties": {
                    "use_cases": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/UseCase"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "post": {
        "operationId": "createUseCase",
        "summary": "createUseCase",
        "description": "Create a new use case for an integration",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateUseCaseRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Use case created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UseCase"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Integration not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/use-cases/{useCaseId}": {
      "get": {
        "operationId": "getUseCase",
        "summary": "getUseCase",
        "description": "Retrieve a specific use case by its ID",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "useCaseId",
            "in": "path",
            "required": true,
            "description": "The use case ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved use case",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UseCase"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Use case not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "put": {
        "operationId": "updateUseCase",
        "summary": "updateUseCase",
        "description": "Update an existing use case configuration",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "useCaseId",
            "in": "path",
            "required": true,
            "description": "The use case ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateUseCaseRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Use case updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UseCase"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Use case not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteUseCase",
        "summary": "deleteUseCase",
        "description": "Delete a use case from an integration",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "useCaseId",
            "in": "path",
            "required": true,
            "description": "The use case ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Use case deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Use case not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/use-cases/{useCaseId}/history": {
      "get": {
        "operationId": "listUseCaseHistory",
        "summary": "listUseCaseHistory",
        "description": "Retrieve historical versions of a use case's configuration.\nHistory entries are returned in reverse chronological order (newest first).\nUse the 'cursor' parameter for pagination to fetch additional entries.\n",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "useCaseId",
            "in": "path",
            "required": true,
            "description": "The use case ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque pagination cursor. Pass the 'next_cursor' value from a previous\nresponse to fetch the next page of results.\n",
            "schema": {
              "type": "string"
            },
            "example": "eyJwayI6Ik9SRyMxMjM0NSIsInNrIjoiSU5URUdSQVRJT04jLi4uIn0="
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved use case history",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "history"
                  ],
                  "properties": {
                    "history": {
                      "type": "array",
                      "description": "History entries in reverse chronological order (newest first)",
                      "items": {
                        "$ref": "#/components/schemas/UseCaseHistoryEntry"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "description": "Opaque cursor for fetching the next page. Absent if no more pages."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Use case not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/documentation": {
      "get": {
        "operationId": "listDocumentationPages",
        "summary": "listDocumentationPages",
        "description": "Retrieve all documentation pages of an integration, without their markdown content.\nAn integration has at most one general page plus at most one page per use case.\nThe page id is 'general' for the integration-wide page, otherwise the use case ID.\n",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved documentation pages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "pages"
                  ],
                  "properties": {
                    "pages": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DocumentationPageSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Integration not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/documentation/{docId}": {
      "get": {
        "operationId": "getDocumentationPage",
        "summary": "getDocumentationPage",
        "description": "Retrieve a single documentation page including its markdown content",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "docId",
            "in": "path",
            "required": true,
            "description": "'general' for the integration-wide page, otherwise a use case ID",
            "schema": {
              "type": "string",
              "pattern": "^(general|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved documentation page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentationPage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Documentation page not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "put": {
        "operationId": "upsertDocumentationPage",
        "summary": "upsertDocumentationPage",
        "description": "Create or update the documentation page identified by docId.\nUpsert semantics enforce the invariant of one general page per\nintegration and one page per use case. For use case pages the\nuse case must exist.\n",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "docId",
            "in": "path",
            "required": true,
            "description": "'general' for the integration-wide page, otherwise a use case ID",
            "schema": {
              "type": "string",
              "pattern": "^(general|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpsertDocumentationPageRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Documentation page created or updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentationPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Integration or use case not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteDocumentationPage",
        "summary": "deleteDocumentationPage",
        "description": "Delete a documentation page",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "docId",
            "in": "path",
            "required": true,
            "description": "'general' for the integration-wide page, otherwise a use case ID",
            "schema": {
              "type": "string",
              "pattern": "^(general|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Documentation page deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Documentation page not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/integrations": {
      "get": {
        "operationId": "listIntegrationsV2",
        "summary": "listIntegrationsV2",
        "description": "Retrieve all integrations with embedded use cases for the authenticated organization",
        "tags": [
          "integrations"
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved integrations with use cases",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "integrations"
                  ],
                  "properties": {
                    "integrations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/IntegrationWithUseCases"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "post": {
        "operationId": "createIntegrationV2",
        "summary": "createIntegrationV2",
        "description": "Create a new integration with embedded use cases.\n",
        "tags": [
          "integrations"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpsertIntegrationWithUseCasesRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Integration created successfully with use cases",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationWithUseCases"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/integrations/{integrationId}": {
      "get": {
        "operationId": "getIntegrationV2",
        "summary": "getIntegrationV2",
        "description": "Retrieve a specific integration with all its embedded use cases",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved integration with use cases",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationWithUseCases"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Integration not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "put": {
        "operationId": "updateIntegrationV2",
        "summary": "updateIntegrationV2",
        "description": "Update an existing integration with embedded use cases.\nThe integration must already exist.\nUse cases are updated declaratively:\n- Use cases in the request with matching IDs are updated\n- Use cases in the request without matching IDs are created\n- Existing use cases not in the request are deleted\n",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID (client-provided)",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpsertIntegrationWithUseCasesRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Integration updated successfully with use cases",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationWithUseCases"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Integration not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteIntegrationV2",
        "summary": "deleteIntegrationV2",
        "description": "Delete an integration and all its use cases",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Integration deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Integration not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/integrations/{integrationId}/notifications/history": {
      "get": {
        "operationId": "listNotificationHistory",
        "summary": "listNotificationHistory",
        "description": "Returns the cursor-paginated, newest-first notification history for an\nintegration (every real notification decision — both fired and suppressed).\nRequires the `integration:view` permission on the integration's organization.\n",
        "tags": [
          "integrations"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque base64 pagination cursor returned as `next_cursor` by a prior page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Requested number of items to return. Values above 100 are accepted and clamped server-side to 100 (the enforcement point is the handler, not this schema), so a large value never 400s at the contract layer.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 50
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Optional notification type filter (e.g. `critical_error`, `error_threshold`).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cursor-paginated notification history (newest first)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationHistoryResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/integrations/{integrationId}/notifications/test": {
      "post": {
        "operationId": "testSendNotification",
        "summary": "testSendNotification",
        "description": "Renders and sends ONE representative notification of the requested kind/type to\nthe CALLING USER ONLY (never any other recipient), so an operator can preview how\na notification looks. A test send does NOT write to the notification history.\nRequires the `integration:manage` permission on the integration's organization.\n",
        "tags": [
          "integrations"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TestNotificationRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Test notification accepted for delivery to the calling user (sent is true)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestNotificationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "502": {
            "description": "The downstream notification service rejected or failed the test send.\nThe body uses TestNotificationResponse with sent set to false so the\noperator sees the real (failed) delivery outcome.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestNotificationResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v2/integrations/{integrationId}/notifications/status": {
      "get": {
        "operationId": "getNotificationStatus",
        "summary": "getNotificationStatus",
        "description": "Returns the live per-rule alert state and (for 'auto' rules) the current\nhour-of-week baseline band for an integration's notification monitoring.\nReflects the latest 5-minute sweep — near-real-time, not live.\nRequires the `integration:view` permission on the integration's organization.\n",
        "tags": [
          "integrations"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "include",
            "in": "query",
            "required": false,
            "description": "Add `baseline_series` to also return all 168 hour-of-week buckets per\n'auto' rule (heavier; omit for just the current-bucket markers).\n",
            "schema": {
              "type": "string",
              "enum": [
                "baseline_series"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Live notification status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationStatusResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/integrations/{integrationId}/use-cases/{useCaseId}/secure-proxy-whitelist": {
      "get": {
        "operationId": "getSecureProxyWhitelist",
        "summary": "getSecureProxyWhitelist",
        "description": "Returns the current allowed_domains, allowed_ips, and vpc_mode for a secure_proxy use case.\nStaff-only — gated by internal-auth issuer AND admin-portal Cognito user pool membership.\nRejects Login-As tokens.\n",
        "tags": [
          "integrations",
          "proxy"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "useCaseId",
            "in": "path",
            "required": true,
            "description": "The use case ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current whitelist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SecureProxyWhitelist"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Use case not found or not of type secure_proxy"
          }
        }
      },
      "put": {
        "operationId": "updateSecureProxyWhitelist",
        "summary": "updateSecureProxyWhitelist",
        "description": "Replaces allowed_domains and/or allowed_ips on a secure_proxy use case.\nAt least one of the two fields is required. Validation mirrors the CLI's\n`validateDomainPatterns` / `validateCidrs`. Writes a USECASE_HISTORY row\nwith the admin user's email as `changed_by`.\n\nUpdate semantics per field:\n  - **omitted** — field is not modified; the stored value is preserved.\n  - **non-empty array** — the stored value is replaced with the supplied list.\n  - **empty array (`[]`)** — the list is cleared (stored as `[]`). This is\n    the canonical way to remove all entries. `null` is not accepted.\n\nStaff-only — same auth gates as GET.\n",
        "tags": [
          "integrations",
          "proxy"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "useCaseId",
            "in": "path",
            "required": true,
            "description": "The use case ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecureProxyWhitelistUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated whitelist (same shape as GET)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SecureProxyWhitelist"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Use case not found or not of type secure_proxy"
          }
        }
      }
    },
    "/v2/integrations/{integrationId}/use-cases/{useCaseId}/secure-proxy-whitelist/history": {
      "get": {
        "operationId": "listSecureProxyWhitelistHistory",
        "summary": "listSecureProxyWhitelistHistory",
        "description": "Returns the most recent USECASE_HISTORY entries for a secure_proxy use case,\nin reverse chronological order (newest first). Each entry includes the\nactor email (`changed_by`), the ISO-8601 timestamp (`history_created_at`),\nthe `change_description` (free-text action), and the full `configuration`\nsnapshot — from which UI-08 computes a before/after diff between consecutive\nentries.\n\nStaff-only — gated by internal-auth issuer AND admin-portal Cognito user pool\nmembership. Rejects Login-As tokens (same auth gate as the GET / PUT\nsecure-proxy-whitelist operations).\n\nThin wrapper over the service-layer `listUseCaseHistory` that powers\n`GET /v1/integrations/{integrationId}/use-cases/{useCaseId}/history` — the\nseparate path exists because the /v1 variant is tenant-gated and admin-portal\ninternal-auth tokens do not carry tenant permissions.\n",
        "tags": [
          "integrations",
          "proxy"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "useCaseId",
            "in": "path",
            "required": true,
            "description": "The use case ID (must be of type secure_proxy)",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of history entries to return. Default 10, max 50.\nCapped at the service-layer page size (20) so `limit > 20` is silently\nclamped to 20. UI-08 requests 5-10 for the panel view.\n",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved secure_proxy history",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "history"
                  ],
                  "properties": {
                    "history": {
                      "type": "array",
                      "description": "Entries are `SecureProxyUseCaseHistoryEntry` in reverse\nchronological order (newest first). Entries older than the\nrequested `limit` are not returned.\n",
                      "items": {
                        "$ref": "#/components/schemas/SecureProxyUseCaseHistoryEntry"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Use case not found or not of type secure_proxy"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/app-mapping": {
      "put": {
        "operationId": "setIntegrationAppMapping",
        "summary": "setIntegrationAppMapping",
        "description": "Creates or updates a mapping from an app/component to an integration.\nThis allows ERP updates sent via app_id and component_id to be associated\nwith a specific integration configuration.\n",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID to map to",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetIntegrationAppMappingRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "App mapping set successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationAppMapping"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Integration not found"
          },
          "409": {
            "description": "Mapping already exists (use overwrite=true to replace)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseBase"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteIntegrationAppMapping",
        "summary": "deleteIntegrationAppMapping",
        "description": "Removes a mapping from an app/component to an integration.\n",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID (used for authorization, must match the mapping)",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeleteIntegrationAppMappingRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "App mapping deleted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "App mapping not found"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/monitoring/inbound-events": {
      "post": {
        "deprecated": true,
        "operationId": "queryInboundMonitoringEvents",
        "summary": "queryInboundMonitoringEvents",
        "description": "**Deprecated and no longer implemented.** The `erp_monitoring` table this\nendpoint read has been retired in favour of the unified `erp_monitoring_v2`\ntable. The endpoint is kept only so existing clients do not 404; it always\nresponds `200` with an empty result and never reads any data.\n\nUse `POST /v2/integrations/{integrationId}/monitoring/events` instead,\nfiltered to `use_case_type: inbound`.\n",
        "tags": [
          "monitoring",
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QueryInboundMonitoringEventsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueryInboundMonitoringEventsResponse"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/monitoring/stats": {
      "post": {
        "deprecated": true,
        "operationId": "getMonitoringStats",
        "summary": "getMonitoringStats",
        "description": "**Deprecated and no longer implemented.** The `erp_monitoring` and\n`webhook_events` tables this endpoint aggregated have been retired in favour\nof the unified `erp_monitoring_v2` table. The endpoint is kept only so\nexisting clients do not 404; it always responds `200` with zeroed counters\nand never reads any data.\n\nUse `POST /v2/integrations/{integrationId}/monitoring/stats` instead.\n",
        "tags": [
          "monitoring",
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GetMonitoringStatsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/GetMonitoringStatsResponse"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/monitoring/timeseries": {
      "post": {
        "deprecated": true,
        "operationId": "getMonitoringTimeSeries",
        "summary": "getMonitoringTimeSeries",
        "description": "**Deprecated and no longer implemented.** The `erp_monitoring` and\n`webhook_events` tables this endpoint bucketed have been retired in favour\nof the unified `erp_monitoring_v2` table. The endpoint is kept only so\nexisting clients do not 404; it always responds `200` with an empty bucket\nlist and never reads any data.\n\nUse `POST /v2/integrations/{integrationId}/monitoring/time-series` instead.\n",
        "tags": [
          "monitoring",
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GetMonitoringTimeSeriesRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/GetMonitoringTimeSeriesResponse"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/outbound-status": {
      "get": {
        "operationId": "getOutboundStatus",
        "summary": "getOutboundStatus",
        "description": "Get the status of all outbound use cases for a specific integration.\nReturns conflict information when events or webhooks are disabled but the use case is enabled.\n",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved outbound status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutboundStatusResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/entities/{entityId}/sync-status": {
      "get": {
        "operationId": "getEntitySyncStatus",
        "summary": "getEntitySyncStatus",
        "description": "Get the inbound ERP sync status of an entity: when each integration last\nsynchronized (checked) the entity against the ERP, and when it last\nactually changed it. `last_synced_at` also advances on no-op checks —\nevents that were received and evaluated but changed nothing — which by\ndesign leave no trace on the entity itself (no activity feed entry, no\n`_updated_at` bump). Use it to tell whether an entity is up to date with\nthe ERP. Returns an empty list for entities no inbound use case has\nprocessed. Org-scoped via the caller's token.\n\nDeprecated alias: `GET /v1/entities/{entityId}/sync-status` is still\nserved (rewritten to this path in the API handler) but will be removed —\nmigrate callers to this path.\n",
        "tags": [
          "integrations"
        ],
        "parameters": [
          {
            "name": "entityId",
            "in": "path",
            "required": true,
            "description": "The entity ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "integration_id",
            "in": "query",
            "required": false,
            "description": "Narrow the result to a single integration",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved entity sync status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EntitySyncStatusResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/outbound/messages/poll": {
      "post": {
        "operationId": "pollOutboundMessages",
        "summary": "pollOutboundMessages",
        "description": "Poll outbound messages for an integration's poll-mode use cases.\nTakes a lease on the head-of-line batch of the integration's FIFO stream:\nthe returned messages stay invisible to subsequent polls until they are\nacknowledged or the visibility timeout elapses. POST because taking a\nlease mutates server state — auto-retrying middleware must not burn\nleases. One in-flight batch per stream: while a lease is active (or\nanother poll wins the race), the response is an empty batch\n(`messages: []`), not an error. Requires the `integration:consume` grant.\n",
        "tags": [
          "integrations"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PollOutboundMessagesRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Leased batch of outbound messages (possibly empty)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PollOutboundMessagesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/outbound/messages/ack": {
      "post": {
        "operationId": "ackOutboundMessages",
        "summary": "ackOutboundMessages",
        "description": "Acknowledge polled outbound messages. Acks are validated against the\nactive lease and committed as a prefix-contiguous cursor advance:\nmessages must be acknowledged in stream order. Out-of-order acks past\nthe first gap are rejected per id (`out_of_order`), acks with an\noutdated lease token are rejected (`stale_lease`), and unknown ids are\nrejected (`not_found`). Acknowledged messages are never delivered\nagain. Requires the `integration:consume` grant.\n",
        "tags": [
          "integrations"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AckOutboundMessagesRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Per-id acknowledgement results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AckOutboundMessagesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/outbound/messages/dlq": {
      "get": {
        "operationId": "listOutboundDlqMessages",
        "summary": "listOutboundDlqMessages",
        "description": "List an integration's dead-lettered outbound queue messages\n(poison_policy enforcement and operator skips move messages here).\nOperator endpoint — requires the `integration:manage` grant. Message\npayloads are NOT included in listings: entries are keyed by the\nopaque message id and carry delivery metadata only. Paginated via an\nopaque `next_token`.\n",
        "tags": [
          "integrations"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of DLQ entries to return",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "next_token",
            "in": "query",
            "required": false,
            "description": "Opaque pagination token from a previous response",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of dead-lettered messages",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OutboundDlqListResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/outbound/messages/dlq/redrive": {
      "post": {
        "operationId": "redriveOutboundDlqMessages",
        "summary": "redriveOutboundDlqMessages",
        "description": "Redrive selected dead-lettered messages back into the live stream.\nOperator endpoint — requires the `integration:manage` grant.\nA redriven message is re-enqueued at the tail with a new id and\nsequence — it is delivered out of its original per-entity order (the\nstream has moved on); this is inherent to redrive and matches SQS DLQ\nsemantics. The redriven copy starts with zero delivery attempts and a\nfresh retention window; the original DLQ entry is removed. Per-id\nresults report `redriven` or `not_found` (unknown ids, or entries\nconcurrently redriven/expired).\n",
        "tags": [
          "integrations"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RedriveOutboundDlqRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Per-id redrive results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RedriveOutboundDlqResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/outbound/messages/unblock": {
      "post": {
        "operationId": "unblockOutboundStream",
        "summary": "unblockOutboundStream",
        "description": "Unblock an integration's outbound stream halted by the `block`\npoison policy: skips (dead-letters) the current blocked head message,\nemitting MSG_DEAD_LETTERED and letting the next message become the\nhead. Operator endpoint — requires the `integration:manage` grant.\nReturns `unblocked: false` as a no-op when the stream is not\ncurrently blocked (or the state moved concurrently) — safe to retry.\n",
        "tags": [
          "integrations"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UnblockOutboundStreamRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Unblock outcome",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnblockOutboundStreamResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/monitoring/access-logs": {
      "post": {
        "operationId": "queryAccessLogs",
        "summary": "queryAccessLogs",
        "description": "Query API access logs for a specific integration's organization.\nReturns access token usage analytics filtered by user_id (access token).\nSupports infinite scroll pagination with cursor-based navigation.\n",
        "tags": [
          "monitoring",
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID (used for tenant authorization)",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QueryAccessLogsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueryAccessLogsResponse"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/monitoring/outbound-events": {
      "post": {
        "deprecated": true,
        "operationId": "queryOutboundMonitoringEvents",
        "summary": "queryOutboundMonitoringEvents",
        "description": "**Deprecated and no longer implemented.** The `webhook_events` table this\nendpoint read has been retired in favour of the unified `erp_monitoring_v2`\ntable. The endpoint is kept only so existing clients do not 404; it always\nresponds `200` with an empty result and never reads any data.\n\nUse `POST /v2/integrations/{integrationId}/monitoring/events` instead,\nfiltered to `use_case_type: outbound`.\n",
        "tags": [
          "monitoring",
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QueryOutboundMonitoringEventsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueryOutboundMonitoringEventsResponse"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/integrations/{integrationId}/monitoring/events": {
      "post": {
        "operationId": "queryMonitoringEventsV2",
        "summary": "queryMonitoringEventsV2",
        "description": "Query monitoring events from the unified erp_monitoring_v2 table.\nReturns all event types (inbound, outbound, file_proxy, etc.) in a single list.\nReplaces the separate v1 inbound-events and outbound-events endpoints.\n",
        "tags": [
          "monitoring",
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QueryMonitoringEventsV2Request"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueryMonitoringEventsV2Response"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/integrations/{integrationId}/monitoring/stats": {
      "post": {
        "operationId": "getMonitoringStatsV2",
        "summary": "getMonitoringStatsV2",
        "description": "Get aggregated statistics from the unified erp_monitoring_v2 table.\nReturns combined metrics for all event types with optional breakdowns.\n",
        "tags": [
          "monitoring",
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GetMonitoringStatsV2Request"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/GetMonitoringStatsV2Response"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/integrations/{integrationId}/monitoring/time-series": {
      "post": {
        "operationId": "getMonitoringTimeSeriesV2",
        "summary": "getMonitoringTimeSeriesV2",
        "description": "Get time-series aggregated event counts from the unified erp_monitoring_v2 table.\nReturns bucketed counts for chart rendering.\n",
        "tags": [
          "monitoring",
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GetMonitoringTimeSeriesV2Request"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/GetMonitoringTimeSeriesV2Response"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/integrations/{integrationId}/monitoring/events/{eventId}/associated": {
      "get": {
        "operationId": "getAssociatedMonitoringEvents",
        "summary": "getAssociatedMonitoringEvents",
        "description": "Returns all monitoring events sharing the same event_id, ordered chronologically.\nAlso includes the original inbound event payload from erp_incoming_events if available.\nUsed to display a full event trace/timeline.\n",
        "tags": [
          "monitoring",
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "eventId",
            "in": "path",
            "required": true,
            "description": "The event ID to get associated events for",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/GetAssociatedMonitoringEventsResponse"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/integrations/{integrationId}/monitoring/external-events": {
      "post": {
        "operationId": "ingestExternalMonitoringEvents",
        "summary": "ingestExternalMonitoringEvents",
        "description": "Ingest monitoring spans produced by an EXTERNAL system (e.g. an integration\nmiddleware), so the Integration Hub is the central monitoring point and the\ncross-system event trace spans both the external system and epilot's own\nprocessing.\n\nEach span is validated, assigned a server-controlled `EXTERNAL_*` taxonomy code\nderived from its `level` (the client never supplies a code — this marks\nprovenance and prevents spoofing an epilot code), has its `use_case_slug`\nresolved against the integration's configured use cases, and is republished onto\nthe same monitoring event bus as epilot's own spans — so external spans are\nfirst-class to the trace view, stats, alerting and digests.\n\nSpans link to the epilot trace via `correlation_id` (the trace id): the middleware\nmust stamp the same `correlation_id` here and on the event it forwards to the\ninbound endpoint. Invalid spans in a batch are reported per-item and do not fail\nthe whole batch.\n",
        "tags": [
          "monitoring",
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IngestExternalMonitoringEventsRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Batch accepted (some spans may have been rejected — see counts).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IngestExternalMonitoringEventsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/integrations/{integrationId}/monitoring/traces/{correlationId}": {
      "get": {
        "operationId": "getMonitoringTraceByCorrelation",
        "summary": "getMonitoringTraceByCorrelation",
        "description": "Returns the cross-system event trace for a `correlation_id`: every monitoring\nspan sharing it — external spans (middleware) plus epilot's own processing spans\n— ordered chronologically, with a rolled-up status and the epilot inbound event\n\"head\" attached. This is the correlation-grouped counterpart to\n`…/events/{eventId}/associated` (which groups a single epilot event's fan-out by\nevent_id). Origin is distinguished by the `EXTERNAL_*` code prefix on external\nspans. Freshness is near-real-time (spans land as they are produced/ingested).\n",
        "tags": [
          "monitoring",
          "integrations"
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "description": "The integration ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "correlationId",
            "in": "path",
            "required": true,
            "description": "The trace id (correlation_id) shared across systems",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The assembled cross-system trace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonitoringTraceResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/secure-proxies": {
      "get": {
        "operationId": "listSecureProxies",
        "summary": "listSecureProxies",
        "description": "Lists all secure_proxy use cases across all integrations for the authenticated organization.\nReturns minimal data suitable for dropdowns and selection UIs.\n",
        "tags": [
          "integrations"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successfully retrieved secure proxy use cases",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "secure_proxies"
                  ],
                  "properties": {
                    "secure_proxies": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecureProxySummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/secure-proxy": {
      "post": {
        "operationId": "secureProxy",
        "summary": "secureProxy",
        "description": "Routes an HTTP request through a VPC with either static IP egress or VPN secure link access.\nThe VPC mode is determined by the referenced secure_proxy use case configuration.\nFor secure_link mode, the target URL must match the use case's allowed_domains whitelist.\n",
        "tags": [
          "proxy"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecureProxyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Proxied response from the target URL",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SecureProxyResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Forbidden - insufficient permissions or target domain not whitelisted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseBase"
                }
              }
            }
          },
          "502": {
            "description": "Upstream failure: epilot could not obtain an HTTP response from the proxied target\n(e.g. TLS handshake failure, connection refused/reset, DNS failure or timeout).\nThe body's `code`/`reason` identify the underlying cause, distinguishing a\nremote-side problem from an epilot-side one. When the target *does* answer with\nits own status, that status and body are passed through unchanged via\n`SecureProxyResponse` instead.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SecureProxyUpstreamError"
                }
              }
            }
          },
          "503": {
            "description": "VPC proxy not available for the requested mode",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseBase"
                }
              }
            }
          },
          "504": {
            "description": "Upstream timeout",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseBase"
                }
              }
            }
          }
        }
      }
    },
    "/v1/managed-call/{slug}/execute": {
      "post": {
        "operationId": "managedCallExecute",
        "summary": "managedCallExecute",
        "description": "Execute a managed call operation synchronously. The slug in the path acts as the RPC method name.\nCalls an external partner API with JSONata mapping on both request and response.\n",
        "tags": [
          "managed-call"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9_-]*$"
            },
            "description": "Use case slug (acts as the RPC method name)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ManagedCallExecuteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Managed call executed successfully.\nThe response body is the JSONata-mapped result (or raw external API response if no mapping).\nIf inbound routing is configured, check X-Inbound-Event-Id header for tracking.\n",
            "headers": {
              "X-Inbound-Event-Id": {
                "description": "Event ID for tracking inbound pipeline processing (only present when inbound routing is configured)",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ManagedCallExecuteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (e.g., invalid URL blocked by SSRF protection)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ManagedCallErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "502": {
            "description": "External API error (upstream service returned an error)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ManagedCallErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/generate-types-preview": {
      "post": {
        "operationId": "generateTypesPreview",
        "summary": "generateTypesPreview",
        "description": "Analyses the JSONata mappings of all managed-call use cases in the integration and returns scaffolded type descriptors. The frontend uses these to show the type editor modal where developers fill in leaf types.\n",
        "tags": [
          "integrations"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Scaffolded type preview",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenerateTypesPreviewResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/generate-types": {
      "post": {
        "operationId": "generateTypes",
        "summary": "generateTypes",
        "description": "Generates a complete TypeScript npm package with typed interfaces for all managed-call use cases. This is a stateless operation that does not persist any changes. Use the commit-types endpoint to lock configurations after review.\n",
        "tags": [
          "integrations"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerateTypesRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generated package",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenerateTypesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/integrations/{integrationId}/commit-types": {
      "post": {
        "operationId": "commitTypes",
        "summary": "commitTypes",
        "description": "Commits the generated types by locking use case configurations and updating version tracking. Should be called after the user reviews and downloads the generated package.\n",
        "tags": [
          "integrations"
        ],
        "security": [
          {
            "EpilotAuth": []
          }
        ],
        "parameters": [
          {
            "name": "integrationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CommitTypesRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Types committed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitTypesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/erp/imports": {
      "post": {
        "operationId": "createErpImport",
        "summary": "createErpImport",
        "description": "Register an already-uploaded file (S3 ref) as a pricing-file import job. Returns the job and a file preview. Nothing runs yet: no use case is chosen and no validation starts here. Optionally rank candidates with POST /v2/erp/imports/{importId}:suggest-use-cases, then start validation with POST /v2/erp/imports/{importId}:validate.\nPass `import_id` to repoint an existing import at a different file instead, keeping its id and its place in the history — allowed while the import has written nothing.",
        "tags": [
          "erp-imports"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateErpImportRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The named import now points at this file. Returned only when `import_id` was supplied — no `Location`, because nothing was created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateErpImportResponse"
                }
              }
            }
          },
          "201": {
            "description": "Import job created. Returned only when `import_id` was omitted.",
            "headers": {
              "Location": {
                "description": "URL of the created job (`/v2/erp/imports/{importId}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateErpImportResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "get": {
        "operationId": "listErpImports",
        "summary": "listErpImports",
        "description": "List recent pricing-file import jobs for the org, newest first.\n\nPass `next_cursor` back as `cursor` for the next page. A page can be\nshorter than `limit` and still have more behind it, so stop on\n`next_cursor: null`.\n",
        "tags": [
          "erp-imports"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Values above 100 are clamped to 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from a prior page's `next_cursor`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Import jobs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErpImportList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/erp/imports/{importId}": {
      "get": {
        "operationId": "getErpImport",
        "summary": "getErpImport",
        "description": "Get a pricing-file import job (status, counts, result links).",
        "tags": [
          "erp-imports"
        ],
        "parameters": [
          {
            "name": "importId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The import job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErpImportJob"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteErpImport",
        "summary": "deleteErpImport",
        "description": "Remove an import and the file it owns. Allowed from any status: an import whose run is still in flight is stopped by the deletion, and rows it already wrote stay written.",
        "tags": [
          "erp-imports"
        ],
        "parameters": [
          {
            "name": "importId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The import and its file were removed"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/erp/imports/{importId}:validate": {
      "post": {
        "operationId": "validateErpImport",
        "summary": "validateErpImport",
        "description": "Choose the use case to read this file with, and start the validate phase.\nCallable from PENDING, READY and FAILED — so a wrong choice is corrected by calling this again with a different use case, rather than re-uploading. Each call replaces any previous verdict. Any other status returns 409.",
        "tags": [
          "erp-imports"
        ],
        "parameters": [
          {
            "name": "importId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ValidateErpImportRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Validation started",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErpImportJob"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "description": "The check could not be run. `code` is the translation key.\n\n- `USE_CASE_NOT_USABLE` — missing, not inbound, disabled, or no mapping\n  configuration.\n- `SCHEMA_NOT_AUTHORITATIVE` — an entity schema could not be read. Retryable.\n\nA mapping that CAN be judged and is found wanting is not a 422: the job is\nrecorded FAILED with `error.code = VALIDATION_BLOCKED` and the detail in\n`validation.issues`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseBase"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/erp/imports/{importId}:suggest-use-cases": {
      "post": {
        "operationId": "suggestErpImportUseCases",
        "summary": "suggestErpImportUseCases",
        "description": "Rank the org's inbound use cases against this file's columns — the input to the ranked picker (\"matches 6 of your 7 columns\"). Optional: skip this and call `:validate` directly when the use case is already known. Reads only the file's first row, not its data.\nEvery eligible use case is returned, including ones matching nothing: the \"nothing fits, pick anyway\" view needs the full list. Highest match first.\nCSV only — an xlsx import fails with 400 rather than silently returning zero matches, which would look identical to \"we checked and nothing matches\".",
        "tags": [
          "erp-imports"
        ],
        "parameters": [
          {
            "name": "importId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ranked use-case suggestions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuggestErpImportUseCasesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/erp/imports/{importId}:execute": {
      "post": {
        "operationId": "executeErpImport",
        "summary": "executeErpImport",
        "description": "Confirm and run the write phase of a validated import. Only a READY job may be executed; any other status returns 409.\nThe verdict is re-checked against live inputs first: if the mapping or an entity schema changed since the check, this returns 409 `VALIDATION_STALE` and leaves the verdict untouched for `:validate` to re-form.",
        "tags": [
          "erp-imports"
        ],
        "parameters": [
          {
            "name": "importId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExecuteErpImportRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Execution started",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErpImportJob"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The job cannot be executed as it stands. `code` is the translation key.\n\n- `IMPORT_NOT_READY` — the job is not READY, or a concurrent confirm won.\n- `VALIDATION_STALE` — the inputs the verdict was formed against have changed;\n  re-run `:validate`.\n\nNeither changes the job's status or its verdict.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseBase"
                }
              }
            }
          },
          "422": {
            "description": "The write could not be started. `code` is the translation key.\n\n- `WARNINGS_NOT_ACKNOWLEDGED` — the verdict carries warnings and `ack_warnings`\n  was not true.\n- `USE_CASE_NOT_USABLE` — the use case went missing, disabled, or lost its\n  mapping configuration after the check.\n- `SCHEMA_NOT_AUTHORITATIVE` — an entity schema could not be read. Retryable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseBase"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v2/erp/imports/{importId}:abort": {
      "post": {
        "operationId": "abortErpImport",
        "summary": "abortErpImport",
        "description": "Ask a running import to stop. Valid while the job is VALIDATING or PROCESSING; any other status returns 409.\nThe stop is cooperative: the job passes through CANCELLING and typically reaches CANCELLED within seconds.\nRows already published to the platform are **kept**: this is a stop, not a rollback. Aborting during VALIDATING has published nothing, so it is always clean; aborting during PROCESSING leaves a partial import, and the rows that landed are visible in monitoring under the job's correlation_id.",
        "tags": [
          "erp-imports"
        ],
        "parameters": [
          {
            "name": "importId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "The stop was recorded. The job is still finishing up — poll GET /v2/erp/imports/{importId} to observe CANCELLED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErpImportJob"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "EpilotAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Authorization header with epilot OAuth2 bearer token",
        "bearerFormat": "JWT"
      }
    },
    "schemas": {
      "S3Reference": {
        "type": "object",
        "required": [
          "bucket",
          "key"
        ],
        "properties": {
          "bucket": {
            "type": "string"
          },
          "key": {
            "type": "string"
          }
        }
      },
      "CreateErpImportRequest": {
        "type": "object",
        "description": "Register an already-uploaded file as an import. The use case is chosen later, via `:validate` — upload and interpretation are separate decisions.",
        "required": [
          "s3_reference"
        ],
        "additionalProperties": false,
        "properties": {
          "s3_reference": {
            "$ref": "#/components/schemas/S3Reference"
          },
          "include_preview": {
            "type": "boolean",
            "default": false,
            "description": "Return a sample of the file's first rows in `preview`. Only controls whether the sample comes BACK — the head is read either way, because that read is how an unreadable file gets refused."
          },
          "import_id": {
            "type": "string",
            "description": "Repoint an EXISTING import at this file instead of registering a new one — same row, same id, so iterating on which file to import leaves one entry in the history rather than one per attempt.\nRepointing is allowed exactly while the import has written nothing: from PENDING and READY, and from FAILED when `error.code` is one of `VALIDATION_BLOCKED`, `VALIDATE_TIMEOUT` or `FILE_FORMAT_UNSUPPORTED`. Anything else returns 409 with code `IMPORT_NOT_REPLACEABLE`. The repoint clears any verdict the import had.\nAn id that names nothing returns 404. It never falls back to registering a new import: a stale id must fail loudly rather than quietly produce a second one."
          }
        }
      },
      "ValidateErpImportRequest": {
        "type": "object",
        "required": [
          "integration_id",
          "use_case_slug"
        ],
        "additionalProperties": false,
        "properties": {
          "integration_id": {
            "type": "string"
          },
          "use_case_slug": {
            "type": "string",
            "description": "An inbound use case's slug, unique within its integration."
          }
        }
      },
      "ErpImportUseCaseSuggestion": {
        "type": "object",
        "required": [
          "integration_id",
          "integration_name",
          "use_case_slug",
          "use_case_name",
          "matched_columns",
          "entity_types"
        ],
        "properties": {
          "integration_id": {
            "type": "string"
          },
          "integration_name": {
            "type": "string"
          },
          "use_case_slug": {
            "type": "string"
          },
          "use_case_name": {
            "type": "string"
          },
          "entity_types": {
            "type": "integer",
            "description": "How many distinct entity schemas this mapping uses."
          },
          "matched_columns": {
            "type": "integer",
            "description": "How many of the FILE's columns this use case reads, by exact trimmed name."
          }
        }
      },
      "SuggestErpImportUseCasesResponse": {
        "type": "object",
        "required": [
          "file_columns",
          "suggestions"
        ],
        "properties": {
          "file_columns": {
            "type": "integer",
            "description": "Columns in the file. Present even when `suggestions` is empty."
          },
          "suggestions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErpImportUseCaseSuggestion"
            }
          }
        }
      },
      "ExecuteErpImportRequest": {
        "type": "object",
        "description": "Confirmation options. Required only when the verdict carries warnings — a clean import needs no body at all.",
        "additionalProperties": false,
        "properties": {
          "ack_warnings": {
            "type": "boolean",
            "description": "The caller has seen `validation.issues` and accepts them. Required when `validation.warnings` is greater than 0; ignored otherwise."
          }
        }
      },
      "ErpImportIssue": {
        "type": "object",
        "description": "A problem found during validation, scoped to the file as a whole rather than to\nindividual rows.\n\n`code` is the translation key and the other fields are its parameters — there is\ndeliberately no message to display. Each code appears at most once, with\neverything it has to say aggregated into that one entry.\n\nThe conditional codes describe a file that cannot be written as variants:\n\n- `TIER_ROWS_NOT_GROUPED` — rows of one variant are spread through the file;\n  `columns` names what to group by.\n- `TIER_BANDS_CONFLICT` — two rows of one variant claim the same band.\n- `CONDITION_VALUE_MISSING` — a condition column is blank in every row.\n- `VARIANT_VALUE_CONFLICT` — rows of one variant disagree on a value that is not\n  the one being folded; the first row's value is what would be written.\n- `ATTRIBUTE_NOT_OVERRIDABLE` — the schema does not let a variant override this\n  attribute, so the mapped value would be discarded.\n- `ATTRIBUTE_NOT_IN_SCHEMA` — the schema does not have the attribute at all.\n\nThe rest reject the mapping against the entity schemas, before the file is read, and\nname the thing at fault in `subject`: `IS_CONDITIONAL_NOT_CONSTANT`,\n`SCHEMA_NOT_CONDITIONABLE`, `SCHEMA_NOT_FOUND`, `SCHEMA_DECLARES_NO_CONDITIONS`,\n`GROUPING_KEY_NOT_A_COLUMN`, `GROUPING_KEY_IS_FOLD_COLUMN`.",
        "required": [
          "code",
          "severity"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Enum of possible issue codes.\n",
            "enum": [
              "UNIQUE_ID_COLUMN_MISSING",
              "MAPPED_COLUMN_MISSING",
              "MALFORMED_ROW",
              "INVALID_ENCODING",
              "EMPTY_FILE",
              "TOO_MANY_ROWS",
              "BLANK_ROWS_SKIPPED",
              "TIER_ROWS_NOT_GROUPED",
              "TIER_BANDS_CONFLICT",
              "CONDITION_VALUE_MISSING",
              "VARIANT_VALUE_CONFLICT",
              "ATTRIBUTE_NOT_OVERRIDABLE",
              "ATTRIBUTE_NOT_IN_SCHEMA",
              "IS_CONDITIONAL_NOT_CONSTANT",
              "SCHEMA_NOT_CONDITIONABLE",
              "SCHEMA_NOT_FOUND",
              "SCHEMA_DECLARES_NO_CONDITIONS",
              "GROUPING_KEY_NOT_A_COLUMN",
              "GROUPING_KEY_IS_FOLD_COLUMN"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "warning",
              "blocking"
            ]
          },
          "columns": {
            "type": "array",
            "description": "The columns this issue is about, at most one entry per column per entity.\nOn UNIQUE_ID_COLUMN_MISSING the file has NONE of these. Do NOT tell the user that adding one of them is enough: a unique id may read several columns through a JSONata expression, and whether it combines them (`A & B`, both needed) or falls back between them (`A ? A : B`, either will do) is not knowable here.",
            "items": {
              "type": "object",
              "required": [
                "name"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "description": "The column name, spelled as the mapping reads it."
                },
                "entity": {
                  "type": "string",
                  "description": "Slug of the entity this column helps identify. UNIQUE_ID_COLUMN_MISSING only."
                }
              }
            }
          },
          "subject": {
            "type": "string",
            "description": "What the issue is about — an attribute, an entity type slug, or an \"attribute reads column\" pair — to substitute into the client's copy for the code. Independent of `columns`; a code can carry both."
          },
          "row": {
            "type": "integer",
            "description": "The offending data row, 1-based as the user counts rows. MALFORMED_ROW only."
          }
        }
      },
      "ErpImportValidation": {
        "type": "object",
        "description": "Validate-phase summary: what the file will create, and whether it may be confirmed. Absent until the validate phase completes. No per-row detail is kept — a rejected file is corrected and imported again.",
        "required": [
          "blocking",
          "warnings",
          "entities"
        ],
        "properties": {
          "total_rows": {
            "type": "integer",
            "description": "Data rows the import will act on. Rows with no value in any column are dropped before they are counted, and reported as BLANK_ROWS_SKIPPED — so this can be lower than the line count of the file.\nABSENT when the verdict was reached without reading the file, as a mapping refused against the entity schemas alone is. Do not default it to zero."
          },
          "blocking": {
            "type": "integer",
            "description": "Blocking problems found, counting per-row ones that are not listed in `issues`."
          },
          "warnings": {
            "type": "integer"
          },
          "entities": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Distinct entities the file expresses, keyed by entity slug."
          },
          "entity_details": {
            "type": "array",
            "description": "What this import will write, one entry per entity instance, capped at 20 in first-appearance order. `entities` stays exact regardless.",
            "items": {
              "$ref": "#/components/schemas/ErpImportEntityDetail"
            }
          },
          "entity_details_truncated": {
            "type": "boolean",
            "description": "More entities exist than `entity_details` lists."
          },
          "issues": {
            "type": "array",
            "description": "Whole-file issues, at most one per `code`. Do not expect the length to match blocking + warnings: those also count per-row problems, which are recorded for support but never listed here. Warnings here are what `ack_warnings` on `:execute` acknowledges.",
            "items": {
              "$ref": "#/components/schemas/ErpImportIssue"
            }
          }
        }
      },
      "ErpImportEntityDetail": {
        "type": "object",
        "description": "One entity instance the import will write, attributed to the mapping target that produces it.",
        "required": [
          "mapping_index",
          "entity_schema",
          "business_key"
        ],
        "properties": {
          "mapping_index": {
            "type": "integer",
            "description": "Which `config.entities[]` target produced this entity — the join key back to the mapping."
          },
          "entity_schema": {
            "type": "string"
          },
          "business_key": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "The entity's identity: its `unique_ids` as the mapping resolved them, keyed by attribute name in the order the target declares them. Values are strings, as they arrive from CSV."
          },
          "variants": {
            "type": "integer",
            "description": "Distinct condition tuples. Absent when the entity is not conditional."
          },
          "versions": {
            "type": "integer",
            "description": "Version writes — one per variant per effective date. Absent when the entity is not conditional."
          }
        }
      },
      "ErpImportProgress": {
        "type": "object",
        "description": "How far the currently running phase has got. Written at every batch boundary, so it advances during long runs rather than only at the end.\n`total_rows` is ABSENT during the validate phase until the file has been read to the end — there is deliberately no counting pass, since that would be a second unbounded read of the whole file. Render an indeterminate indicator while it is missing: dividing by a missing total yields a determinate bar pinned at 0%, which reads as a hung import.",
        "required": [
          "processed_rows"
        ],
        "properties": {
          "processed_rows": {
            "type": "integer",
            "description": "Rows fully processed. An exact \"the first N rows are done\" watermark, not an estimate — it only advances once a batch has been completely handled."
          },
          "total_rows": {
            "type": "integer",
            "description": "Rows in the file. Known only once a phase has read to EOF; the execute phase has it from the start, because validate recorded it first."
          }
        }
      },
      "ErpImportError": {
        "type": "object",
        "description": "Why the import failed — present if and only if status = FAILED. `code` is the translation key; for VALIDATION_BLOCKED the specifics are in `validation`.",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Enum of possible error codes.\n",
            "enum": [
              "VALIDATION_BLOCKED",
              "FILE_FORMAT_UNSUPPORTED",
              "FILE_UNAVAILABLE",
              "VALIDATE_TIMEOUT",
              "IMPORT_TIMEOUT",
              "USE_CASE_NOT_USABLE",
              "IMPORT_NO_PROGRESS",
              "TIER_ROWS_NOT_GROUPED",
              "INTERNAL_ERROR"
            ]
          },
          "message": {
            "type": "string",
            "description": "One English sentence, derived from `code` so the two always agree. A fallback for a client that has no translation for this code — prefer translating `code`, and never parse this. It deliberately does NOT restate `validation.issues`."
          }
        }
      },
      "ErpImportFilePreview": {
        "type": "object",
        "description": "Sample of the file's first rows, using the same parser as `:validate`. Registration refuses a file it cannot read, so a created job always includes this.",
        "required": [
          "columns",
          "rows"
        ],
        "properties": {
          "columns": {
            "type": "array",
            "description": "Effective column names (trimmed, duplicates collapsed).",
            "items": {
              "type": "string"
            }
          },
          "rows": {
            "type": "array",
            "description": "Up to the first 3 data rows, aligned to `columns`.",
            "items": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        }
      },
      "CreateErpImportResponse": {
        "type": "object",
        "required": [
          "job"
        ],
        "properties": {
          "job": {
            "$ref": "#/components/schemas/ErpImportJob"
          },
          "preview": {
            "$ref": "#/components/schemas/ErpImportFilePreview"
          }
        }
      },
      "ErpImportJob": {
        "type": "object",
        "required": [
          "import_id",
          "org_id",
          "format",
          "status",
          "s3_input_ref",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "import_id": {
            "type": "string",
            "description": "`imp_{ULID}` — time-ordered, also used as the job's correlation_id."
          },
          "org_id": {
            "type": "string"
          },
          "created_by": {
            "type": "string"
          },
          "integration_id": {
            "type": "string",
            "description": "The integration owning the chosen use case. Absent until the first `:validate`."
          },
          "use_case_slug": {
            "type": "string",
            "description": "The inbound use case whose mapping drives both phases. Absent until the first `:validate`, and MUTABLE — re-validating with a different use case replaces it."
          },
          "format": {
            "type": "string",
            "enum": [
              "csv",
              "xlsx"
            ]
          },
          "status": {
            "type": "string",
            "description": "PENDING → VALIDATING → READY → PROCESSING → IMPORTED, with FAILED reachable from any working status, and CANCELLING → CANCELLED reachable from VALIDATING or PROCESSING via :abort. Only IMPORTED and CANCELLED are terminal. READY and FAILED both accept a further `:validate`, which is how a wrong use case is corrected without re-uploading the file.\nIMPORTED means every row was handed to the platform, not that the platform finished — per-row outcomes live in monitoring, filtered by correlation_id. A file that fails validation is FAILED with error.code = VALIDATION_BLOCKED.\nREADY is legitimately idle for as long as the user takes to confirm, so it carries no running work and never goes stale.\nCANCELLING is transient and cooperative: the abort has been recorded but the worker only notices at its next batch boundary. Rows already published stay published — a stop is not a rollback.",
            "enum": [
              "PENDING",
              "VALIDATING",
              "READY",
              "PROCESSING",
              "IMPORTED",
              "FAILED",
              "CANCELLING",
              "CANCELLED"
            ]
          },
          "s3_input_ref": {
            "$ref": "#/components/schemas/S3Reference"
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64",
            "description": "Size of the uploaded file, recorded at registration. Present on every job registered from version 1.17.0 onwards; absent on older rows, which were written before it was captured."
          },
          "column_count": {
            "type": "integer",
            "description": "How many effective columns the file's header yielded — the length of the `preview.columns` returned at registration, kept so a job loaded later can still describe its file. The preview ROWS are deliberately not stored: they are a sample for the person about to choose a mapping, not job state. Present on every job registered from version 1.17.0 onwards."
          },
          "validation": {
            "$ref": "#/components/schemas/ErpImportValidation"
          },
          "progress": {
            "$ref": "#/components/schemas/ErpImportProgress"
          },
          "error": {
            "$ref": "#/components/schemas/ErpImportError"
          },
          "correlation_id": {
            "type": "string",
            "description": "Scopes this run in monitoring. Always equal to `import_id`."
          },
          "activity_id": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ErpImportList": {
        "type": "object",
        "required": [
          "results"
        ],
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErpImportJob"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Cursor for the next page, or null when there are no more rows."
          }
        }
      },
      "NotificationHistoryItem": {
        "type": "object",
        "description": "A single notification-history row (one real notification decision).",
        "required": [
          "id",
          "type",
          "severity",
          "title",
          "occurred_at",
          "notified",
          "recipients",
          "context",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable history row id (ULID)."
          },
          "type": {
            "type": "string",
            "description": "The notification/rule type (e.g. critical_error, error_threshold, integration_digest)."
          },
          "state_transition": {
            "type": "string",
            "nullable": true,
            "description": "The state transition that produced this decision (e.g. OK->ALERTING), when applicable."
          },
          "severity": {
            "type": "string",
            "enum": [
              "error",
              "warning",
              "info"
            ],
            "description": "Severity of the decision."
          },
          "title": {
            "type": "string",
            "description": "Human-readable title at decision time."
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the decision occurred (newest-first ordering key)."
          },
          "notified": {
            "type": "boolean",
            "description": "Whether the notification was actually sent (true) or suppressed (false)."
          },
          "suppressed_reason": {
            "type": "string",
            "nullable": true,
            "enum": [
              "muted",
              "debounced",
              "recipient_opt_out"
            ],
            "description": "Why the notification was suppressed (only set when notified is false)."
          },
          "recipients": {
            "type": "array",
            "description": "epilot user ids the notification was (or would have been) delivered to.",
            "items": {
              "type": "string"
            }
          },
          "context": {
            "type": "object",
            "additionalProperties": true,
            "description": "Type-specific context captured at decision time."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the history row was written."
          }
        }
      },
      "NotificationHistoryResponse": {
        "type": "object",
        "required": [
          "history"
        ],
        "properties": {
          "history": {
            "type": "array",
            "description": "Notification-history rows, newest first.",
            "items": {
              "$ref": "#/components/schemas/NotificationHistoryItem"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Opaque base64 cursor for the next page, or null when there are no more rows."
          }
        }
      },
      "TestNotificationRequest": {
        "type": "object",
        "required": [
          "kind"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "alert",
              "digest"
            ],
            "description": "The kind of notification to render and send."
          },
          "type": {
            "type": "string",
            "description": "The alert type to render when kind=alert (e.g. critical_error, error_threshold)."
          },
          "channels": {
            "type": "array",
            "description": "Delivery channels to use; defaults to the integration's configured channels.",
            "items": {
              "type": "string",
              "enum": [
                "email",
                "in_app"
              ]
            }
          }
        }
      },
      "TestNotificationResponse": {
        "type": "object",
        "required": [
          "sent",
          "recipient",
          "channels"
        ],
        "properties": {
          "sent": {
            "type": "boolean",
            "description": "Whether the test notification was accepted for delivery."
          },
          "recipient": {
            "type": "string",
            "description": "The calling user id the test was sent to (the only recipient)."
          },
          "channels": {
            "type": "array",
            "description": "The channels the test was delivered on.",
            "items": {
              "type": "string"
            }
          },
          "notification_id": {
            "type": "string",
            "nullable": true,
            "description": "The svc-notification-api message id, or null when not returned."
          }
        }
      },
      "NotificationStatusResponse": {
        "type": "object",
        "required": [
          "health",
          "rules"
        ],
        "properties": {
          "health": {
            "type": "string",
            "enum": [
              "healthy",
              "alerting",
              "muted"
            ],
            "description": "Rolled-up live status: `muted` when muteUntil is in the future; else `alerting` if any rule is currently ALERTING; else `healthy`.\n"
          },
          "evaluated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Most recent per-rule evaluation instant (max lastEvaluatedAt), or null when no rule has been evaluated. Updates on the 5-minute sweep tick.\n"
          },
          "rules": {
            "type": "array",
            "description": "Per-rule status, one entry per configured rule.",
            "items": {
              "$ref": "#/components/schemas/NotificationRuleStatus"
            }
          }
        }
      },
      "NotificationRuleStatus": {
        "type": "object",
        "required": [
          "rule_id",
          "state"
        ],
        "properties": {
          "rule_id": {
            "type": "string",
            "description": "The rule's stable id (matches the configured rule id)."
          },
          "state": {
            "type": "string",
            "enum": [
              "ok",
              "alerting",
              "recovered"
            ],
            "description": "The rule's live AlertState (defaults to `ok` when never evaluated)."
          },
          "last_fired_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "ISO instant the rule last entered ALERTING."
          },
          "last_cleared_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "ISO instant the rule last cleared back to OK."
          },
          "last_evaluation": {
            "type": "object",
            "nullable": true,
            "description": "The most recent evaluation of this rule, or null when it has never been evaluated. Answers \"why isn't this rule firing?\" for a rule that neither fires nor transitions — which otherwise leaves no trace at all.",
            "required": [
              "evaluated_at",
              "observed"
            ],
            "properties": {
              "evaluated_at": {
                "type": "string",
                "format": "date-time",
                "description": "ISO instant of the evaluation. Updates on every sweep tick."
              },
              "observed": {
                "type": "number",
                "description": "The measure seen (error/warning count, success rate, or event total)."
              },
              "threshold": {
                "type": "number",
                "nullable": true,
                "description": "What it was compared against; null for a rule type with no threshold."
              },
              "suppressed_reason": {
                "type": "string",
                "enum": [
                  "sample_size"
                ],
                "description": "Present ONLY when the rule could not be evaluated. `sample_size` means the success-rate minimum-sample guard was not met, so the rule is neither healthy nor alerting — it is not evaluating at all. Absent when the evaluation ran normally."
              }
            }
          },
          "baseline": {
            "nullable": true,
            "description": "Present only for enabled 'auto'-threshold rules; null otherwise.",
            "allOf": [
              {
                "$ref": "#/components/schemas/RuleBaselineStatus"
              }
            ]
          }
        }
      },
      "RuleBaselineStatus": {
        "type": "object",
        "required": [
          "is_mature"
        ],
        "properties": {
          "is_mature": {
            "type": "boolean",
            "description": "False during cold start; the sweeper uses the static fallbackThreshold until the baseline's history span is mature.\n"
          },
          "computed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "ISO instant the baseline was last computed."
          },
          "median": {
            "type": "number",
            "nullable": true,
            "description": "Typical in-scope event volume for the current hour-of-week bucket."
          },
          "mad": {
            "type": "number",
            "nullable": true,
            "description": "Median absolute deviation for the current hour-of-week bucket."
          },
          "upper": {
            "type": "number",
            "nullable": true,
            "description": "Dynamic alert threshold (median + k·MAD, k by sensitivity) for the current hour-of-week, or null when the bucket is uncovered.\n"
          },
          "buckets": {
            "type": "array",
            "nullable": true,
            "description": "Full 168-bucket series; only present when ?include=baseline_series.",
            "items": {
              "$ref": "#/components/schemas/RuleBaselineBucket"
            }
          }
        }
      },
      "RuleBaselineBucket": {
        "type": "object",
        "required": [
          "dow",
          "hour",
          "median",
          "mad"
        ],
        "properties": {
          "dow": {
            "type": "integer",
            "minimum": 1,
            "maximum": 7,
            "description": "Day of week, 1=Monday … 7=Sunday."
          },
          "hour": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "Hour of day, 0 … 23 (UTC)."
          },
          "median": {
            "type": "number"
          },
          "mad": {
            "type": "number"
          }
        }
      },
      "ErrorResponseBase": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Computer-readable error code"
          },
          "message": {
            "type": "string",
            "description": "Error message"
          }
        }
      },
      "ErpEvent": {
        "type": "object",
        "required": [
          "event_type",
          "object_type",
          "timestamp",
          "format",
          "payload"
        ],
        "properties": {
          "event_type": {
            "type": "string",
            "enum": [
              "CREATE",
              "UPDATE",
              "DELETE"
            ],
            "description": "Type of event (create, update, delete)"
          },
          "object_type": {
            "type": "string",
            "description": "Type of the object being updated (business_partner, contract_account, etc.). Corresponds to \"Event Name\" from the integration UI."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the event occurred"
          },
          "format": {
            "type": "string",
            "enum": [
              "json",
              "xml"
            ],
            "default": "json",
            "description": "Format of the payload data"
          },
          "payload": {
            "oneOf": [
              {
                "type": "string",
                "minLength": 2,
                "description": "The serialized object data payload (JSON, XML, etc.) as a string"
              },
              {
                "type": "object",
                "minProperties": 1,
                "description": "Direct JSON object (will be automatically serialized)",
                "additionalProperties": true
              }
            ],
            "description": "The object data payload - can be either a serialized string or a direct JSON object"
          },
          "deduplication_id": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9_-]+$",
            "minLength": 1,
            "maxLength": 255,
            "description": "Optional unique identifier for idempotency - prevents duplicate processing of the same event within 24 hours in context of the same integration. Must contain only alphanumeric characters, hyphens, and underscores.\n",
            "example": "evt-2025-05-01-12345-create-bp"
          },
          "correlation_id": {
            "type": "string",
            "description": "Optional per-event trace id for cross-system tracing (unique per business operation). Overrides the request-level meta.correlation_id for THIS event. When absent, the event inherits the request-level correlation_id; when both are absent, epilot mints its own event_id and the trace is epilot-only. Orthogonal to deduplication_id (idempotency).\n",
            "example": "bp-8f3a2c-7d4e-4b1a-9c2f-1e6d5a4b3c21"
          },
          "use_case_id": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Resolved use case ID for the inbound event. Null when no use case matched or for events ingested before this field was introduced. Server-populated only — ignored if supplied on inbound requests.\n"
          }
        }
      },
      "ErpUpdatesEventsV2Request": {
        "type": "object",
        "required": [
          "integration_id",
          "events"
        ],
        "properties": {
          "integration_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID that identifies the integration configuration to use"
          },
          "correlation_id": {
            "type": "string",
            "description": "Optional ID that identifies the specific request for debugging purposes"
          },
          "events": {
            "type": "array",
            "description": "List of ERP events to process",
            "items": {
              "$ref": "#/components/schemas/ErpEvent"
            }
          }
        }
      },
      "ErpEventV3": {
        "type": "object",
        "required": [
          "timestamp",
          "format",
          "payload"
        ],
        "anyOf": [
          {
            "required": [
              "event_name"
            ]
          },
          {
            "required": [
              "use_case_slug"
            ]
          }
        ],
        "properties": {
          "event_name": {
            "type": "string",
            "description": "Event name from integration mapping (e.g., business_partner, contract_account). Required when use_case_slug is not provided.\n",
            "example": "business_partner"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the event occurred",
            "example": "2025-05-01T08:30:00Z"
          },
          "format": {
            "type": "string",
            "enum": [
              "json",
              "xml"
            ],
            "default": "json",
            "description": "Format of the payload data",
            "example": "json"
          },
          "payload": {
            "oneOf": [
              {
                "type": "string",
                "minLength": 2,
                "description": "The serialized object data payload (JSON, XML, etc.) as a string"
              },
              {
                "type": "object",
                "minProperties": 1,
                "description": "Direct JSON object (will be automatically serialized)",
                "additionalProperties": true
              }
            ],
            "description": "The object data payload - can be either a serialized string or a direct JSON object.\n\nFor events routed to a mapped (default) use case this is the raw ERP object; the use\ncase's mapping configuration transforms it into entity updates.\n\nFor events routed to a use case whose configuration has `direct: true`, the payload\nMUST be a `DirectPayload` (as an object, or a JSON string): a versioned envelope of\npre-mapped entity and meter reading operations that skip the mapping engine entirely.\nDirect use cases accept JSON only - `format: \"xml\"` events are rejected.\n",
            "example": "{\"id\":\"BP10001\",\"name\":\"Acme Corporation\",\"type\":\"organization\",\"tax_id\":\"DE123456789\",\"status\":\"active\"}"
          },
          "use_case_slug": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9_-]*$",
            "minLength": 1,
            "maxLength": 255,
            "description": "Recommended. Use case slug for routing this event to the correct use case configuration. If provided, takes precedence over event_name for use case lookup. Preferred over event_name-based routing as slugs are portable across environments.\n",
            "example": "business_partner"
          },
          "deduplication_id": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9_-]+$",
            "minLength": 1,
            "maxLength": 255,
            "description": "Optional unique identifier for idempotency - prevents duplicate processing of the same event within 24 hours in context of the same integration. Must contain only alphanumeric characters, hyphens, and underscores.\n",
            "example": "evt-2025-05-01-12345-bp"
          },
          "group_id": {
            "type": "string",
            "description": "Optional per-event ordering group. Overrides the request-level `group_id` for THIS event; when absent, the event inherits the request-level `group_id` (or the default strict per-integration ordering). Use it when a single batch carries events for unrelated business objects that may be processed in parallel. See the request-level `group_id` for semantics.\n",
            "maxLength": 128,
            "example": "customer-42"
          },
          "correlation_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Optional per-event trace id. Overrides the request-level `correlation_id` for THIS event, so a batch carrying several distinct business operations keeps its traces segregated in monitoring instead of merged under one request-level id. When absent, the event inherits the request-level `correlation_id`.\n",
            "example": "customer-42-sync-2026-08-26"
          }
        }
      },
      "ErpUpdatesEventsV3Request": {
        "type": "object",
        "required": [
          "integration_id",
          "events"
        ],
        "properties": {
          "integration_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID that identifies the integration configuration to use"
          },
          "correlation_id": {
            "type": "string",
            "description": "Optional ID that identifies the specific request for debugging purposes"
          },
          "group_id": {
            "type": "string",
            "description": "Controls ordering and parallelism for this request's events.\n\nBy default, all events for a given `integration_id` are processed\n**strictly in order, one at a time**. For high-volume integrations this\ncan become a throughput bottleneck.\n\nSet `group_id` to opt into **parallel processing**:\n- Events sharing the same `group_id` are processed in the order received.\n- Events with different `group_id` values are processed in parallel.\n\nTypical usage is to derive `group_id` from a logical partition key in\nyour payload — for example the customer ID, contract ID, or meter ID —\nso updates to the same business object remain ordered while unrelated\nobjects are processed concurrently.\n\nNotes:\n- Individual events may set their own `group_id`, which overrides this\n  request-level value for that event only.\n- Up to 20 groups per integration are processed concurrently. Using\n  more distinct values than that yields no additional parallelism.\n- Omit this field if strict per-integration ordering is required.\n",
            "maxLength": 128,
            "example": "customer-42"
          },
          "events": {
            "type": "array",
            "description": "List of ERP events to process",
            "items": {
              "$ref": "#/components/schemas/ErpEventV3"
            }
          }
        }
      },
      "TriggerErpActionRequest": {
        "type": "object",
        "required": [
          "execution_id",
          "org_id",
          "webhook_id",
          "flow_id",
          "created_at",
          "action_id",
          "flow_action_id",
          "flow_name",
          "activity_id",
          "entity_id"
        ],
        "properties": {
          "execution_id": {
            "type": "string",
            "description": "Unique identifier of the current automation execution"
          },
          "org_id": {
            "type": "string",
            "description": "Identifier of the organization where the automation is executed"
          },
          "webhook_id": {
            "type": "string",
            "description": "Identifier of the self-service webhook configuration"
          },
          "flow_id": {
            "type": "string",
            "description": "Identifier of the automation flow that triggered the action"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO-8601 timestamp when the webhook event was created"
          },
          "action_id": {
            "type": "string",
            "description": "Identifier of the automation action being executed"
          },
          "flow_action_id": {
            "type": "string",
            "description": "Identifier of the specific automation flow action instance"
          },
          "flow_name": {
            "type": "string",
            "description": "Human readable name of the automation flow"
          },
          "activity_id": {
            "type": "string",
            "description": "Identifier of the entity activity related to this trigger"
          },
          "entity_id": {
            "type": "string",
            "description": "Identifier of the entity referenced by the activity"
          }
        }
      },
      "TriggerWebhookResp": {
        "type": "object",
        "properties": {
          "status_code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "body": {
            "type": "object"
          },
          "code": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "succeeded",
              "failed"
            ]
          },
          "start_date": {
            "type": "string"
          },
          "end_date": {
            "type": "string"
          },
          "event_id": {
            "type": "string"
          }
        }
      },
      "IntegrationEditableFields": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Integration name"
          },
          "description": {
            "type": "string",
            "maxLength": 1000,
            "description": "Optional description of the integration"
          },
          "access_token_ids": {
            "type": "array",
            "description": "List of access token IDs associated with this integration",
            "items": {
              "type": "string"
            }
          },
          "app_ids": {
            "type": "array",
            "description": "List of app IDs associated with this integration",
            "items": {
              "type": "string"
            }
          },
          "environment_config": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EnvironmentFieldConfig"
            },
            "description": "Configuration defining environment variables needed by this integration. Values are stored in the Environments API."
          },
          "maps": {
            "type": "array",
            "description": "Re-usable key/value maps (e.g. salutation codes) declared by this integration (max 50). Each map is synced to the Environments API as a non-secret variable of type `JSON` and is available in JSONata mappings as `$env.<key>`, together with the `$mapValue(map, key, default)` and `$mapKey(map, value, default)` helpers.\n",
            "items": {
              "$ref": "#/components/schemas/IntegrationMap"
            }
          },
          "settings": {
            "$ref": "#/components/schemas/IntegrationSettings"
          },
          "integration_type": {
            "type": "string",
            "enum": [
              "erp",
              "connector"
            ],
            "default": "erp",
            "description": "Type of integration. \"erp\" is the ERP integration with inbound/outbound use cases. \"connector\" is for complex proxy integrations with external APIs.\n"
          },
          "connector_config": {
            "$ref": "#/components/schemas/ConnectorConfig"
          },
          "protected": {
            "type": "boolean",
            "description": "If true, integration is displayed in read-only mode in the UI to discourage changes"
          },
          "_manifest": {
            "type": "array",
            "description": "The manifest IDs associated with this integration",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ConnectorConfig": {
        "type": "object",
        "description": "Shared configuration for connector-type integrations",
        "properties": {
          "base_url": {
            "type": "string",
            "description": "Base URL for the partner API"
          },
          "auth": {
            "$ref": "#/components/schemas/ManagedCallAuth"
          },
          "types_versions": {
            "type": "array",
            "description": "History of generated type package versions",
            "items": {
              "type": "object",
              "required": [
                "version",
                "package_name",
                "generated_at",
                "generated_by",
                "status"
              ],
              "properties": {
                "version": {
                  "type": "string"
                },
                "package_name": {
                  "type": "string"
                },
                "generated_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "generated_by": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "active",
                    "deprecated"
                  ]
                }
              }
            }
          },
          "latest_types_version": {
            "type": "string",
            "description": "Latest active types package version"
          },
          "latest_types_package_name": {
            "type": "string",
            "description": "Latest active types package name"
          }
        }
      },
      "ManagedCallAuth": {
        "type": "object",
        "description": "Authentication configuration for managed call requests",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "oauth2_client_credentials",
              "api_key",
              "bearer"
            ],
            "description": "Authentication type"
          },
          "token_url": {
            "type": "string",
            "description": "OAuth2 token URL. Can be plain text or {{env.key}} reference."
          },
          "client_id": {
            "type": "string",
            "description": "OAuth2 client ID. Can be plain text or {{env.key}} reference."
          },
          "client_secret": {
            "type": "string",
            "description": "OAuth2 client secret. Must be an {{env.key}} reference (secret)."
          },
          "scope": {
            "type": "string",
            "description": "OAuth2 scope"
          },
          "audience": {
            "type": "string",
            "description": "OAuth2 audience parameter (e.g. for Auth0, Azure AD). Can be plain text or {{env.key}} reference."
          },
          "resource": {
            "type": "string",
            "description": "OAuth2 resource parameter (e.g. for Azure AD). Can be plain text or {{env.key}} reference."
          },
          "body_params": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Additional key-value pairs for the OAuth2 token request body. Values can be {{env.key}} references."
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Additional headers for the OAuth2 token request. Values can be {{env.key}} references."
          },
          "query_params": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Additional query parameters for the OAuth2 token URL. Values can be {{env.key}} references."
          },
          "api_key_header": {
            "type": "string",
            "description": "Header name for API key auth (default X-API-Key)"
          },
          "api_key": {
            "type": "string",
            "description": "API key value. Must be an {{env.key}} reference (secret)."
          },
          "token": {
            "type": "string",
            "description": "Bearer token value. Must be an {{env.key}} reference (secret)."
          }
        }
      },
      "Integration": {
        "allOf": [
          {
            "type": "object",
            "required": [
              "id",
              "orgId",
              "name",
              "created_at",
              "updated_at"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "readOnly": true,
                "description": "Unique identifier for the integration"
              },
              "orgId": {
                "type": "string",
                "readOnly": true,
                "description": "Organization ID"
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "readOnly": true,
                "description": "ISO-8601 timestamp when the integration was created"
              },
              "updated_at": {
                "type": "string",
                "format": "date-time",
                "readOnly": true,
                "description": "ISO-8601 timestamp when the integration was last updated"
              }
            }
          },
          {
            "$ref": "#/components/schemas/IntegrationEditableFields"
          }
        ]
      },
      "CreateIntegrationRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/IntegrationEditableFields"
          },
          {
            "type": "object",
            "required": [
              "name"
            ]
          }
        ]
      },
      "UpdateIntegrationRequest": {
        "$ref": "#/components/schemas/IntegrationEditableFields"
      },
      "IntegrationMap": {
        "type": "object",
        "description": "A flat key/value map stored as an Environments API variable of type `JSON`. Values must be strings, numbers, booleans or null; the serialised map must not exceed 32 KB.\n",
        "required": [
          "key",
          "label",
          "value"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "Environment variable key the map is synced to (`$env.<key>` in JSONata).",
            "pattern": "^[a-z0-9][a-z0-9_.\\-]{0,127}$"
          },
          "label": {
            "type": "string",
            "description": "Display label for the map in the UI",
            "minLength": 1,
            "maxLength": 255
          },
          "description": {
            "type": "string",
            "description": "Help text shown for the map",
            "maxLength": 1000
          },
          "value": {
            "type": "object",
            "description": "The map itself, e.g. `{\"Mr.\": 1, \"Ms. / Mrs.\": 2}`",
            "additionalProperties": true
          }
        }
      },
      "EnvironmentFieldConfig": {
        "type": "object",
        "required": [
          "key",
          "label",
          "type"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "Environment variable key, used to look up the value in the Environments API.",
            "pattern": "^[a-z0-9][a-z0-9_.\\-]{0,127}$"
          },
          "label": {
            "type": "string",
            "description": "Display label for the field in the UI",
            "minLength": 1,
            "maxLength": 255
          },
          "type": {
            "type": "string",
            "enum": [
              "String",
              "SecretString"
            ],
            "description": "Whether the value is a plain string or an encrypted secret"
          },
          "description": {
            "type": "string",
            "description": "Help text shown below the field",
            "maxLength": 1000
          },
          "required": {
            "type": "boolean",
            "default": false,
            "description": "Whether this field must be filled before the integration can be used"
          },
          "order": {
            "type": "integer",
            "minimum": 0,
            "description": "Sort order for display and drag-to-reorder"
          }
        }
      },
      "IntegrationSettings": {
        "type": "object",
        "description": "Settings for the integration",
        "properties": {
          "autoRefresh": {
            "$ref": "#/components/schemas/AutoRefreshSettings"
          },
          "notifications": {
            "$ref": "#/components/schemas/IntegrationNotificationConfig"
          }
        }
      },
      "AutoRefreshSettings": {
        "type": "object",
        "description": "Auto-refresh settings for keeping integration data fresh",
        "properties": {
          "enabled": {
            "type": "boolean",
            "default": false,
            "description": "Whether auto-refresh is enabled"
          },
          "freshnessThresholdMinutes": {
            "type": "integer",
            "minimum": 1,
            "description": "Maximum age (in minutes) of data before it is considered stale and eligible for refresh"
          }
        }
      },
      "IntegrationNotificationConfig": {
        "type": "object",
        "description": "Integration monitoring notification configuration. Rides Integration.settings.notifications (camelCase) and surfaces on both v1 and v2 GET/PUT. Unknown keys are stripped server-side to stay forward-compatible with deferred (V2) rule types.",
        "required": [
          "enabled",
          "recipients",
          "defaultChannels",
          "rules",
          "digest"
        ],
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Master switch for this integration's notifications."
          },
          "recipients": {
            "type": "array",
            "description": "epilot user ids notified for this integration. Same-org membership and per-user notification preferences are enforced at send time (Phases 3–5), not at config-write time.",
            "items": {
              "$ref": "#/components/schemas/NotificationRecipient"
            }
          },
          "defaultChannels": {
            "$ref": "#/components/schemas/NotificationChannelSet"
          },
          "monitoredUseCases": {
            "type": "array",
            "description": "Integration-level use-case include-filter; absent/empty means all use cases.",
            "items": {
              "type": "string"
            }
          },
          "monitoredCodes": {
            "type": "array",
            "description": "Integration-level code scope; absent/empty resolves to ['_error_']. Accepts concrete monitoring error codes or group sentinels (_error_, _warning_, _success_, _info_, _any_, _parent_).",
            "items": {
              "type": "string"
            }
          },
          "rules": {
            "type": "array",
            "description": "Enabled triggers and their params. A type MAY repeat; capped at 20 rules (enforced at the write boundary).",
            "items": {
              "$ref": "#/components/schemas/NotificationRule"
            }
          },
          "digest": {
            "$ref": "#/components/schemas/NotificationDigestConfig"
          },
          "muteUntil": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "ISO instant; snooze all non-digest alerts until this time. `null` means not muted."
          }
        }
      },
      "NotificationRecipient": {
        "type": "object",
        "description": "A configured recipient. Only the epilot user_id is stored.",
        "required": [
          "user_id"
        ],
        "properties": {
          "user_id": {
            "type": "string",
            "description": "epilot user id. Same-org membership is enforced at send time (Phases 3–5), which re-validates each recipient against the integration's org before fanning out — it is not enforced at config-write time."
          }
        }
      },
      "NotificationChannelSet": {
        "type": "object",
        "description": "Delivery channel toggles. New channels added in svc-notification-api inherit here.",
        "required": [
          "email",
          "in_app"
        ],
        "properties": {
          "email": {
            "type": "boolean"
          },
          "in_app": {
            "type": "boolean"
          }
        }
      },
      "NotificationRule": {
        "type": "object",
        "description": "A single notification rule. Only the params relevant to a given type are set. The id is optional on write: the server mints a ULID when omitted and preserves a supplied id verbatim (a stable id keeps a rule's AlertState across config saves).",
        "required": [
          "type",
          "enabled"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable AlertState + baseline key. Optional on write — the server mints a ULID when omitted; a supplied id is preserved verbatim."
          },
          "name": {
            "type": "string",
            "description": "Optional human label disambiguating two rules of the same type."
          },
          "type": {
            "type": "string",
            "description": "Rule trigger type. These are the only supported types; each is produced by a real alerter.",
            "enum": [
              "critical_error",
              "error_threshold",
              "warning_threshold",
              "success_rate_drop",
              "recovery",
              "silence"
            ]
          },
          "enabled": {
            "type": "boolean"
          },
          "channels": {
            "$ref": "#/components/schemas/NotificationChannelSet"
          },
          "codes": {
            "type": "array",
            "description": "Per-rule code scope. Event-matching rules default to ['_parent_']; silence defaults to ['_any_']. success_rate_drop and recovery take no codes.",
            "items": {
              "type": "string"
            }
          },
          "threshold": {
            "description": "Count or percentage; 'auto' selects anomaly-baseline mode.",
            "oneOf": [
              {
                "type": "number"
              },
              {
                "type": "string",
                "enum": [
                  "auto"
                ]
              }
            ]
          },
          "sensitivity": {
            "type": "string",
            "description": "Band width for 'auto' mode.",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          },
          "fallbackThreshold": {
            "type": "number",
            "description": "Static value used while the 'auto' baseline is immature (cold start)."
          },
          "window": {
            "type": "string",
            "description": "Evaluation window, e.g. '15m', '1h', '24h'."
          },
          "minSampleSize": {
            "type": "integer",
            "description": "success_rate_drop minimum sample size guard."
          },
          "quietPeriod": {
            "type": "string",
            "description": "silence quiet period, e.g. '12h'."
          }
        }
      },
      "NotificationDigestConfig": {
        "type": "object",
        "description": "Digest schedule and content configuration.",
        "required": [
          "enabled",
          "frequency",
          "timeOfDay",
          "timezone",
          "channels",
          "includeHealthy",
          "skipIfEmpty"
        ],
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "frequency": {
            "type": "string",
            "enum": [
              "daily",
              "weekly"
            ]
          },
          "dayOfWeek": {
            "type": "integer",
            "description": "Weekly only. 0 = Sunday … 6 = Saturday.",
            "enum": [
              0,
              1,
              2,
              3,
              4,
              5,
              6
            ]
          },
          "timeOfDay": {
            "type": "string",
            "description": "HH:mm"
          },
          "timezone": {
            "type": "string",
            "description": "IANA timezone, e.g. 'Europe/Berlin'."
          },
          "channels": {
            "$ref": "#/components/schemas/NotificationChannelSet"
          },
          "includeHealthy": {
            "type": "boolean",
            "description": "List all integrations vs. only ones with issues."
          },
          "skipIfEmpty": {
            "type": "boolean",
            "description": "Suppress the digest when nothing happened."
          }
        }
      },
      "SetIntegrationAppMappingRequest": {
        "type": "object",
        "required": [
          "app_id",
          "component_id"
        ],
        "properties": {
          "app_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the integration app instance"
          },
          "component_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the integration app component instance"
          },
          "overwrite": {
            "type": "boolean",
            "default": false,
            "description": "If true, overwrites existing mapping. If false and mapping exists, returns 409 Conflict."
          }
        }
      },
      "DeleteIntegrationAppMappingRequest": {
        "type": "object",
        "required": [
          "app_id",
          "component_id"
        ],
        "properties": {
          "app_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the integration app instance"
          },
          "component_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the integration app component instance"
          }
        }
      },
      "IntegrationAppMapping": {
        "type": "object",
        "required": [
          "integration_id"
        ],
        "properties": {
          "integration_id": {
            "type": "string",
            "format": "uuid",
            "description": "The integration ID this app/component is mapped to"
          }
        }
      },
      "IntegrationWithUseCases": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Integration"
          },
          {
            "type": "object",
            "description": "Integration with embedded use cases for atomic CRUD operations",
            "required": [
              "use_cases"
            ],
            "properties": {
              "use_cases": {
                "type": "array",
                "description": "All use cases belonging to this integration",
                "items": {
                  "$ref": "#/components/schemas/UseCase"
                }
              }
            }
          }
        ]
      },
      "UpsertIntegrationWithUseCasesRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/IntegrationEditableFields"
          },
          {
            "type": "object",
            "description": "Request to create or update an integration with embedded use cases (upsert).\nThis is a declarative operation - the request represents the desired state.\n",
            "required": [
              "name"
            ],
            "properties": {
              "use_cases": {
                "type": "array",
                "description": "Full list of use cases (declarative). This replaces ALL existing use cases.\n- Use cases with an `id` field matching an existing use case will be updated\n- Use cases without an `id` or with a non-matching `id` will be created\n- Existing use cases not in this list will be deleted\n",
                "items": {
                  "$ref": "#/components/schemas/EmbeddedUseCaseRequest"
                }
              }
            }
          }
        ]
      },
      "InboundIntegrationEventConfiguration": {
        "type": "object",
        "description": "Configuration for inbound use cases (ERP to epilot)",
        "properties": {
          "direct": {
            "type": "boolean",
            "description": "Enables direct mode for this use case. When `true`, every event routed to this use\ncase must carry a `DirectPayload` - pre-mapped entity and meter reading operations -\ninstead of raw ERP data, and the mapping engine is skipped entirely. Everything else\nin the inbound pipeline (deduplication, ordering, create-vs-update resolution,\nrelation resolution, monitoring) behaves exactly as in mapped mode.\n\nIn direct mode, `entities` doubles as an optional allowlist: when non-empty, each\noperation's `entity_slug` must match an entry's `entity_schema`, and when that entry\ndeclares `unique_ids`, the operation's unique-id keys must be exactly that set (or\njust `_id`). Entries need only `entity_schema` and `unique_ids` - `fields` is not\nrequired and is ignored. `meter_readings` is ignored in direct mode.\n\nFlag changes take up to 5 minutes to propagate (configuration cache).\n"
          },
          "entities": {
            "type": "array",
            "description": "Array of entity configurations for this event",
            "items": {
              "$ref": "#/components/schemas/IntegrationEntity"
            }
          },
          "meter_readings": {
            "type": "array",
            "description": "Array of meter reading configurations for this event",
            "items": {
              "$ref": "#/components/schemas/IntegrationMeterReading"
            }
          }
        }
      },
      "OutboundIntegrationEventConfiguration": {
        "type": "object",
        "required": [
          "event_catalog_event",
          "mappings"
        ],
        "description": "Configuration for outbound use cases. Defines the event that triggers the flow and the webhook mappings.",
        "properties": {
          "event_catalog_event": {
            "type": "string",
            "description": "The Event Catalog event name that triggers this outbound flow",
            "example": "contract.created"
          },
          "event_filter": {
            "type": "string",
            "description": "JSONata boolean predicate over the hydrated event payload. The use case handles the\nevent only when this evaluates truthy; when absent it handles every event of its name,\nwhich is the behaviour of every pre-existing configuration.\n\nThis is where event scoping belongs — narrowing to certain ticket purposes, contract\ntypes or channels. Evaluation input is the full hydrated event, so relation nodes such\nas `ticket` and `contact` are populated. An expression that throws is treated as no\nmatch and logged, so one malformed filter cannot block the other use cases subscribed\nto the same event.\n",
            "example": "$count(ticket._purpose[$ in ['bae4b4d1-d728-49ef-92ff-54486dd301b4']]) > 0"
          },
          "mappings": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/OutboundMapping"
            },
            "description": "List of mappings that transform and deliver the event"
          },
          "ack_tracking": {
            "type": "string",
            "enum": [
              "on",
              "off"
            ],
            "default": "on",
            "description": "Whether this use case participates in the acknowledgement protocol.\n\n`on` (the default, and the behaviour of every pre-existing use case) records an\nACK_PENDING on each event and expects the consumer to confirm receipt via\n`POST /v1/erp/tracking/acknowledgement`; unconfirmed events raise ACK_TIMEOUT after\nthe timeout window.\n\n`off` opts the use case out entirely: no tracking row, no ACK_PENDING, no\nACK_TIMEOUT. Set it for consumers that never acknowledge — otherwise every event\nproduces a guaranteed timeout warning — and for deliveries that already keep their\nown durable per-item record, such as `file_proxy`.\n\nThe tracking row is per-event, not per-use-case, so it is suppressed only when EVERY\nenabled use case matching the event has opted out.\n"
          }
        }
      },
      "IntegrationEntity": {
        "type": "object",
        "required": [
          "entity_schema",
          "unique_ids"
        ],
        "properties": {
          "entity_schema": {
            "type": "string",
            "description": "Target entity schema (e.g., 'contact', 'contract')"
          },
          "unique_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Array of attribute names that uniquely identify this entity.\nThe _type hint for repeatable fields (e.g., email, phone) should be specified\non the corresponding field definition in the fields array.\n"
          },
          "jsonataExpression": {
            "type": "string",
            "description": "Optional JSONata expression to pre-process the event data before field mapping"
          },
          "enabled": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string"
              }
            ],
            "description": "Controls whether this entity mapping should be processed. Can be a boolean or a JSONata expression (string) that evaluates to a boolean."
          },
          "mode": {
            "type": "string",
            "enum": [
              "upsert",
              "delete",
              "purge",
              "upsert-prune-scope-purge",
              "upsert-prune-scope-delete"
            ],
            "default": "upsert",
            "description": "Operation mode for entity mapping:\n- 'upsert': Create or update the entity (default)\n- 'delete': Soft delete the entity (marks as deleted)\n- 'purge': Hard delete the entity (permanent removal)\n- 'upsert-prune-scope-purge': Upsert entities from array, then purge entities in scope that weren't upserted\n- 'upsert-prune-scope-delete': Upsert entities from array, then soft delete entities in scope that weren't upserted\n"
          },
          "scope": {
            "$ref": "#/components/schemas/PruneScopeConfig"
          },
          "fields": {
            "type": "array",
            "description": "Field mapping definitions. Required for mapped-mode entity configurations; may be\nomitted for allowlist entries of a `direct: true` use case, where no mapping runs.\n",
            "items": {
              "$ref": "#/components/schemas/IntegrationEntityField"
            }
          },
          "conditional": {
            "$ref": "#/components/schemas/IntegrationEntityConditional"
          }
        }
      },
      "IntegrationEntityConditional": {
        "type": "object",
        "description": "Conditional Pricing extras for this target, used when it writes a conditional entity (a Product, Price or Coupon carrying context-dependent variants). The conditions themselves are declared on the entity schema, not here.",
        "required": [
          "folds"
        ],
        "properties": {
          "folds": {
            "type": "array",
            "minItems": 1,
            "maxItems": 1,
            "description": "Row folds for this target. Exactly one is supported; the array is the shape for later, not a capability claim.",
            "items": {
              "$ref": "#/components/schemas/IntegrationEntityFold"
            }
          }
        }
      },
      "IntegrationEntityFold": {
        "type": "object",
        "description": "Collapse the rows of one variant-version into a single array attribute, ordered by a column. A commodity file carries one row per consumption band, while a variant-version holds exactly one `tiers` array.",
        "required": [
          "attribute",
          "sort_by",
          "item"
        ],
        "properties": {
          "attribute": {
            "type": "string",
            "description": "The array attribute this fold builds, e.g. `tiers`."
          },
          "sort_by": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MappedFieldValue"
              }
            ],
            "description": "Orders the group's rows. Coerced to a number, so `10000` sorts after `5000` rather than before it as strings."
          },
          "item": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/MappedFieldValue"
            },
            "description": "Attribute name to field value, shaping one array item from one row. `jsonataExpression` is often needed here: German ERP exports carry decimal commas (`31,25`) that `Number()` cannot parse."
          }
        }
      },
      "MappedFieldValue": {
        "description": "One mapped value. Exactly one of field, constant or jsonataExpression must be set — the same three forms `IntegrationEntityField` accepts.",
        "oneOf": [
          {
            "type": "object",
            "required": [
              "field"
            ],
            "properties": {
              "field": {
                "type": "string",
                "description": "Source column name, or a JSONPath expression when it starts with $"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "constant"
            ],
            "properties": {
              "constant": {
                "description": "Constant value, of any type"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "jsonataExpression"
            ],
            "properties": {
              "jsonataExpression": {
                "type": "string",
                "description": "JSONata expression evaluated against the row"
              }
            }
          }
        ]
      },
      "IntegrationMeterReading": {
        "type": "object",
        "required": [
          "meter",
          "fields"
        ],
        "properties": {
          "jsonataExpression": {
            "type": "string",
            "description": "Optional JSONata expression to extract meter reading items from the event data.\nIf not provided, the entire payload is used as the reading data.\nUseful when you need to extract an array of readings from a nested structure (e.g., \"$.readings\").\n"
          },
          "reading_matching": {
            "type": "string",
            "enum": [
              "external_id",
              "strict-date"
            ],
            "default": "external_id",
            "description": "Strategy for matching incoming readings against existing readings.\n- 'external_id': Match readings by external_id attribute (default behavior)\n- 'strict-date': Match by meter_id + counter_id + direction + date (German timezone).\n  Useful when readings originate from ECP and are echoed back by the ERP with truncated timestamps.\n"
          },
          "mode": {
            "type": "string",
            "enum": [
              "upsert",
              "delete",
              "upsert-prune-scope"
            ],
            "default": "upsert",
            "description": "Operation mode for meter reading mapping:\n- 'upsert': Create or update meter readings (default)\n- 'delete': Delete the meter reading\n- 'upsert-prune-scope': Upsert readings from array, then delete all other readings for the same meter+counter that weren't upserted\n"
          },
          "scope": {
            "$ref": "#/components/schemas/MeterReadingPruneScopeConfig"
          },
          "meter": {
            "$ref": "#/components/schemas/MeterUniqueIdsConfig"
          },
          "meter_counter": {
            "$ref": "#/components/schemas/MeterUniqueIdsConfig"
          },
          "fields": {
            "type": "array",
            "description": "Field mapping definitions for meter reading attributes",
            "items": {
              "$ref": "#/components/schemas/IntegrationEntityField"
            }
          }
        }
      },
      "PruneScopeConfig": {
        "type": "object",
        "description": "Scope configuration for upsert-prune-scope modes.\nDefines how to find entities that should be pruned if not in the upsert payload.\nThe scope is resolved against the original event payload (not individual array items).\n",
        "required": [
          "scope_mode"
        ],
        "properties": {
          "scope_mode": {
            "type": "string",
            "enum": [
              "relations",
              "query"
            ],
            "description": "Scope mode for finding entities to prune:\n- 'relations': Find scope by looking at all entities related to a specific entity (both direct and reverse relations)\n- 'query': Find scope entities directly via query parameters\n"
          },
          "schema": {
            "type": "string",
            "description": "For 'relations' mode: The schema of the entity to find (e.g., 'billing_account').\nNot used for 'query' mode.\n"
          },
          "unique_ids": {
            "type": "array",
            "description": "For 'relations' mode: How to identify the scope entity from the payload.\nNot used for 'query' mode.\n",
            "items": {
              "$ref": "#/components/schemas/RelationUniqueIdField"
            }
          },
          "query": {
            "type": "array",
            "description": "For 'query' mode: Direct query parameters to find scope entities.\nNot used for 'relations' or 'reverse-relations' modes.\n",
            "items": {
              "$ref": "#/components/schemas/RelationUniqueIdField"
            }
          }
        }
      },
      "MeterReadingPruneScopeConfig": {
        "type": "object",
        "description": "Scope configuration for meter reading upsert-prune-scope mode.\nThe scope is all readings for the same meter + counter.\n",
        "properties": {
          "source": {
            "type": "string",
            "description": "Optional source filter. When set, only readings with this source\nare eligible for pruning (e.g., 'ERP' to only prune ERP-synced readings).\n"
          }
        }
      },
      "MeterUniqueIdsConfig": {
        "type": "object",
        "required": [
          "unique_ids"
        ],
        "properties": {
          "unique_ids": {
            "type": "array",
            "minItems": 1,
            "description": "Array of unique identifier field mappings",
            "items": {
              "$ref": "#/components/schemas/RelationUniqueIdField"
            }
          }
        }
      },
      "IntegrationEntityField": {
        "type": "object",
        "required": [
          "attribute"
        ],
        "properties": {
          "attribute": {
            "type": "string",
            "description": "Target attribute name"
          },
          "field": {
            "type": "string",
            "description": "Source field name or JSONPath expression (if starts with $)"
          },
          "jsonataExpression": {
            "type": "string",
            "description": "JSONata expression for transformation"
          },
          "constant": {
            "description": "Constant value to assign (any type)"
          },
          "_type": {
            "$ref": "#/components/schemas/RepeatableFieldType"
          },
          "enabled": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "string"
              }
            ],
            "description": "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."
          },
          "relations": {
            "$ref": "#/components/schemas/RelationConfig"
          },
          "relation_refs": {
            "$ref": "#/components/schemas/RelationRefsConfig"
          },
          "file_proxy_url": {
            "$ref": "#/components/schemas/FileProxyUrlConfig"
          },
          "portal_ref": {
            "$ref": "#/components/schemas/PortalRefConfig"
          },
          "env_var_ref": {
            "$ref": "#/components/schemas/EnvVarRefConfig"
          }
        }
      },
      "FileProxyUrlParam": {
        "description": "Parameter for file proxy URL. Exactly one of field, constant, or jsonataExpression must be set.",
        "oneOf": [
          {
            "type": "object",
            "required": [
              "field"
            ],
            "properties": {
              "field": {
                "type": "string",
                "description": "Source field name or JSONPath expression (if starts with $)"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "constant"
            ],
            "properties": {
              "constant": {
                "description": "Constant value (any type, stringified for URL)"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "jsonataExpression"
            ],
            "properties": {
              "jsonataExpression": {
                "type": "string",
                "description": "JSONata expression for transformation"
              }
            }
          }
        ]
      },
      "FileProxyUrlParams": {
        "type": "object",
        "description": "Custom query parameters. Keys become URL param names, values resolved from payload.",
        "additionalProperties": {
          "$ref": "#/components/schemas/FileProxyUrlParam"
        }
      },
      "FileProxyUrlConfig": {
        "description": "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.\n",
        "oneOf": [
          {
            "type": "object",
            "required": [
              "use_case_slug"
            ],
            "properties": {
              "use_case_slug": {
                "type": "string",
                "pattern": "^[a-z0-9][a-z0-9_-]*$",
                "description": "Recommended. Slug of the file_proxy use case. Maps to useCaseSlug query parameter. Portable across environments.\n"
              },
              "params": {
                "$ref": "#/components/schemas/FileProxyUrlParams"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "use_case_id"
            ],
            "properties": {
              "use_case_id": {
                "type": "string",
                "description": "Legacy. UUID of the file_proxy use case. Maps to useCaseId query parameter. Prefer use_case_slug for portable configuration.\n"
              },
              "params": {
                "$ref": "#/components/schemas/FileProxyUrlParams"
              }
            }
          }
        ]
      },
      "PortalOrigin": {
        "type": "string",
        "enum": [
          "END_CUSTOMER_PORTAL",
          "INSTALLER_PORTAL",
          "B2B_PORTAL",
          "ADDITIONAL_PORTAL"
        ],
        "description": "Origin/type of an epilot portal configuration."
      },
      "PortalRefFilter": {
        "type": "object",
        "description": "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.\n",
        "properties": {
          "origin": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PortalOrigin"
              },
              {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/PortalOrigin"
                }
              }
            ],
            "description": "Single origin or array of origins (matches if origin is in the array)."
          },
          "enabled": {
            "type": "boolean",
            "nullable": true,
            "description": "Match portals with this `enabled` value. Default `true`. Set to `null` to ignore."
          },
          "is_dummy": {
            "type": "boolean",
            "nullable": true,
            "description": "Match portals with this `is_dummy` value. Default `false`. Set to `null` to ignore."
          },
          "is_epilot_domain": {
            "type": "boolean",
            "description": "Optional restriction on `is_epilot_domain`."
          },
          "name": {
            "type": "string",
            "description": "Exact match on the portal's `name`."
          },
          "domain": {
            "type": "string",
            "description": "Exact match on the portal's `domain`."
          }
        }
      },
      "PortalRefConfig": {
        "description": "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.\n",
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "filter": {
                "$ref": "#/components/schemas/PortalRefFilter"
              },
              "select": {
                "type": "string",
                "enum": [
                  "single",
                  "all"
                ],
                "default": "single",
                "description": "`single` returns one literal value (the oldest matching portal); `all` returns an array of literal values (0, 1, or many).\n"
              },
              "return": {
                "type": "string",
                "enum": [
                  "portal_id",
                  "origin",
                  "domain",
                  "name"
                ],
                "default": "portal_id",
                "description": "Which portal field to emit."
              }
            }
          },
          {
            "type": "object",
            "required": [
              "return",
              "jsonataExpression"
            ],
            "properties": {
              "filter": {
                "$ref": "#/components/schemas/PortalRefFilter"
              },
              "select": {
                "type": "string",
                "enum": [
                  "single",
                  "all"
                ],
                "default": "single"
              },
              "return": {
                "type": "string",
                "enum": [
                  "jsonata"
                ],
                "description": "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.\n"
              },
              "jsonataExpression": {
                "type": "string",
                "description": "JSONata expression evaluated against the matched portal(s)."
              }
            }
          }
        ]
      },
      "EnvVarRefConfig": {
        "description": "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.\n",
        "oneOf": [
          {
            "type": "object",
            "required": [
              "key"
            ],
            "properties": {
              "key": {
                "type": "string",
                "pattern": "^[a-z0-9][a-z0-9_.-]{0,127}$",
                "description": "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`.\n"
              },
              "default": {
                "type": "string",
                "description": "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.\n"
              },
              "return": {
                "type": "string",
                "enum": [
                  "value"
                ],
                "default": "value",
                "description": "`value` returns the resolved env-var string as-is.\n"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "key",
              "return",
              "jsonataExpression"
            ],
            "properties": {
              "key": {
                "type": "string",
                "pattern": "^[a-z0-9][a-z0-9_.-]{0,127}$"
              },
              "default": {
                "type": "string"
              },
              "return": {
                "type": "string",
                "enum": [
                  "jsonata"
                ],
                "description": "Evaluate the sibling `jsonataExpression` against the resolved env-var value (the input `$` is the string value).\n"
              },
              "jsonataExpression": {
                "type": "string",
                "description": "JSONata expression evaluated against the resolved env-var value."
              }
            }
          }
        ]
      },
      "EmbeddedUseCaseRequest": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/EmbeddedInboundUseCaseRequest"
          },
          {
            "$ref": "#/components/schemas/EmbeddedOutboundUseCaseRequest"
          },
          {
            "$ref": "#/components/schemas/EmbeddedFileProxyUseCaseRequest"
          },
          {
            "$ref": "#/components/schemas/EmbeddedManagedCallUseCaseRequest"
          },
          {
            "$ref": "#/components/schemas/EmbeddedSecureProxyUseCaseRequest"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "inbound": "#/components/schemas/EmbeddedInboundUseCaseRequest",
            "outbound": "#/components/schemas/EmbeddedOutboundUseCaseRequest",
            "file_proxy": "#/components/schemas/EmbeddedFileProxyUseCaseRequest",
            "managed_call": "#/components/schemas/EmbeddedManagedCallUseCaseRequest",
            "secure_proxy": "#/components/schemas/EmbeddedSecureProxyUseCaseRequest"
          }
        }
      },
      "EmbeddedUseCaseRequestBase": {
        "type": "object",
        "required": [
          "name",
          "type",
          "enabled"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Optional use case ID for update matching.\n- If provided and matches an existing use case, that use case is updated\n- If provided but no match, a new use case with this ID is created\n- If omitted, a new use case with auto-generated ID is created\n"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Use case name"
          },
          "slug": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9_-]*$",
            "minLength": 1,
            "maxLength": 255,
            "description": "URL-safe identifier for the use case. Optional on this upsert/sync endpoint — when omitted, the server derives one from the name and ensures uniqueness within the integration. Immutable after creation.\n"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the use case is enabled"
          },
          "change_description": {
            "type": "string",
            "maxLength": 2000,
            "description": "Optional description of this change (like a commit message)"
          }
        }
      },
      "EmbeddedInboundUseCaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EmbeddedUseCaseRequestBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "inbound"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/InboundIntegrationEventConfiguration"
              }
            }
          }
        ]
      },
      "EmbeddedOutboundUseCaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EmbeddedUseCaseRequestBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "outbound"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/OutboundIntegrationEventConfiguration"
              }
            }
          }
        ]
      },
      "EmbeddedFileProxyUseCaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EmbeddedUseCaseRequestBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "file_proxy"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/FileProxyUseCaseConfiguration"
              }
            }
          }
        ]
      },
      "EmbeddedManagedCallUseCaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EmbeddedUseCaseRequestBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "managed_call"
                ],
                "description": "Use case type for managed API calls"
              },
              "configuration": {
                "$ref": "#/components/schemas/ManagedCallOperationConfig"
              }
            }
          }
        ]
      },
      "EmbeddedSecureProxyUseCaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EmbeddedUseCaseRequestBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "secure_proxy"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/SecureProxyUseCaseConfiguration"
              }
            }
          }
        ]
      },
      "UseCaseBase": {
        "type": "object",
        "required": [
          "id",
          "integrationId",
          "name",
          "type",
          "enabled",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the use case"
          },
          "integrationId": {
            "type": "string",
            "format": "uuid",
            "description": "Parent integration ID"
          },
          "name": {
            "type": "string",
            "description": "Use case name"
          },
          "slug": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9_-]*$",
            "minLength": 1,
            "maxLength": 255,
            "description": "URL-safe identifier for the use case. Recommended for portable cross-environment referencing. Unique per integration. Immutable after creation. Lowercase alphanumeric, hyphens, and underscores only.\n"
          },
          "type": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound",
              "file_proxy",
              "managed_call",
              "secure_proxy"
            ],
            "description": "Use case type"
          },
          "enabled": {
            "type": "boolean"
          },
          "change_description": {
            "type": "string",
            "maxLength": 2000,
            "description": "Reason given for the update that produced the current version. Absent when the update supplied none — it is not inherited from the previous version.\n"
          },
          "changed_by": {
            "type": "string",
            "description": "User ID recorded for the update that produced the current version. Absent when the update had no user context, e.g. an automated sync.\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO-8601 timestamp when the use case was created"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO-8601 timestamp when the use case was last updated"
          }
        }
      },
      "InboundUseCase": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UseCaseBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "inbound"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/InboundIntegrationEventConfiguration"
              }
            }
          }
        ]
      },
      "OutboundUseCase": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UseCaseBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "outbound"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/OutboundIntegrationEventConfiguration"
              }
            }
          }
        ]
      },
      "FileProxyUseCase": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UseCaseBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "file_proxy"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/FileProxyUseCaseConfiguration"
              }
            }
          }
        ]
      },
      "ManagedCallUseCase": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UseCaseBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "managed_call"
                ],
                "description": "Use case type for managed API calls"
              },
              "configuration": {
                "$ref": "#/components/schemas/ManagedCallOperationConfig"
              },
              "type_annotations": {
                "$ref": "#/components/schemas/TypeAnnotations"
              },
              "types_locked": {
                "type": "boolean",
                "description": "Whether types have been generated for this use case"
              }
            }
          }
        ]
      },
      "SecureProxyUseCase": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UseCaseBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "secure_proxy"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/SecureProxyUseCaseConfiguration"
              }
            }
          }
        ]
      },
      "UseCase": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/InboundUseCase"
          },
          {
            "$ref": "#/components/schemas/OutboundUseCase"
          },
          {
            "$ref": "#/components/schemas/FileProxyUseCase"
          },
          {
            "$ref": "#/components/schemas/ManagedCallUseCase"
          },
          {
            "$ref": "#/components/schemas/SecureProxyUseCase"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "inbound": "#/components/schemas/InboundUseCase",
            "outbound": "#/components/schemas/OutboundUseCase",
            "file_proxy": "#/components/schemas/FileProxyUseCase",
            "managed_call": "#/components/schemas/ManagedCallUseCase",
            "secure_proxy": "#/components/schemas/SecureProxyUseCase"
          }
        }
      },
      "CreateUseCaseRequest": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/CreateInboundUseCaseRequest"
          },
          {
            "$ref": "#/components/schemas/CreateOutboundUseCaseRequest"
          },
          {
            "$ref": "#/components/schemas/CreateFileProxyUseCaseRequest"
          },
          {
            "$ref": "#/components/schemas/CreateManagedCallUseCaseRequest"
          },
          {
            "$ref": "#/components/schemas/CreateSecureProxyUseCaseRequest"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "inbound": "#/components/schemas/CreateInboundUseCaseRequest",
            "outbound": "#/components/schemas/CreateOutboundUseCaseRequest",
            "file_proxy": "#/components/schemas/CreateFileProxyUseCaseRequest",
            "managed_call": "#/components/schemas/CreateManagedCallUseCaseRequest",
            "secure_proxy": "#/components/schemas/CreateSecureProxyUseCaseRequest"
          }
        }
      },
      "CreateUseCaseRequestBase": {
        "type": "object",
        "required": [
          "name",
          "slug",
          "type",
          "enabled"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Use case name"
          },
          "slug": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9_-]*$",
            "minLength": 1,
            "maxLength": 255,
            "description": "URL-safe identifier for the use case. Required for explicit creates so every use case has a portable cross-environment identifier. Must be unique per integration. Immutable after creation. Lowercase alphanumeric, hyphens, and underscores only.\n"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the use case is enabled"
          }
        }
      },
      "CreateInboundUseCaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CreateUseCaseRequestBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "inbound"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/InboundIntegrationEventConfiguration"
              }
            }
          }
        ]
      },
      "CreateOutboundUseCaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CreateUseCaseRequestBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "outbound"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/OutboundIntegrationEventConfiguration"
              }
            }
          }
        ]
      },
      "CreateFileProxyUseCaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CreateUseCaseRequestBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "file_proxy"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/FileProxyUseCaseConfiguration"
              }
            }
          }
        ]
      },
      "CreateManagedCallUseCaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CreateUseCaseRequestBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "managed_call"
                ],
                "description": "Use case type for managed API calls"
              },
              "configuration": {
                "$ref": "#/components/schemas/ManagedCallOperationConfig"
              }
            }
          }
        ]
      },
      "CreateSecureProxyUseCaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CreateUseCaseRequestBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "secure_proxy"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/SecureProxyUseCaseConfiguration"
              }
            }
          }
        ]
      },
      "UpdateUseCaseRequest": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/UpdateInboundUseCaseRequest"
          },
          {
            "$ref": "#/components/schemas/UpdateOutboundUseCaseRequest"
          },
          {
            "$ref": "#/components/schemas/UpdateFileProxyUseCaseRequest"
          },
          {
            "$ref": "#/components/schemas/UpdateManagedCallUseCaseRequest"
          },
          {
            "$ref": "#/components/schemas/UpdateSecureProxyUseCaseRequest"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "inbound": "#/components/schemas/UpdateInboundUseCaseRequest",
            "outbound": "#/components/schemas/UpdateOutboundUseCaseRequest",
            "file_proxy": "#/components/schemas/UpdateFileProxyUseCaseRequest",
            "managed_call": "#/components/schemas/UpdateManagedCallUseCaseRequest",
            "secure_proxy": "#/components/schemas/UpdateSecureProxyUseCaseRequest"
          }
        }
      },
      "UpdateUseCaseRequestBase": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Use case name"
          },
          "slug": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9_-]*$",
            "minLength": 1,
            "maxLength": 255,
            "description": "URL-safe identifier for the use case. Recommended for portable cross-environment referencing. Can only be set once on use cases that don't have a slug yet. Immutable after being set.\n"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the use case is enabled"
          },
          "change_description": {
            "type": "string",
            "maxLength": 2000,
            "description": "Optional description of this change (like a commit message)"
          }
        }
      },
      "UpdateInboundUseCaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UpdateUseCaseRequestBase"
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "inbound"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/InboundIntegrationEventConfiguration"
              }
            }
          }
        ]
      },
      "UpdateOutboundUseCaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UpdateUseCaseRequestBase"
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "outbound"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/OutboundIntegrationEventConfiguration"
              }
            }
          }
        ]
      },
      "UpdateFileProxyUseCaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UpdateUseCaseRequestBase"
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "file_proxy"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/FileProxyUseCaseConfiguration"
              }
            }
          }
        ]
      },
      "UpdateManagedCallUseCaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UpdateUseCaseRequestBase"
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "managed_call"
                ],
                "description": "Use case type for managed API calls"
              },
              "configuration": {
                "$ref": "#/components/schemas/ManagedCallOperationConfig"
              },
              "type_annotations": {
                "$ref": "#/components/schemas/TypeAnnotations"
              }
            }
          }
        ]
      },
      "UpdateSecureProxyUseCaseRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UpdateUseCaseRequestBase"
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "secure_proxy"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/SecureProxyUseCaseConfiguration"
              }
            }
          }
        ]
      },
      "UseCaseHistoryEntry": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/InboundUseCaseHistoryEntry"
          },
          {
            "$ref": "#/components/schemas/OutboundUseCaseHistoryEntry"
          },
          {
            "$ref": "#/components/schemas/FileProxyUseCaseHistoryEntry"
          },
          {
            "$ref": "#/components/schemas/ManagedCallUseCaseHistoryEntry"
          },
          {
            "$ref": "#/components/schemas/SecureProxyUseCaseHistoryEntry"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "inbound": "#/components/schemas/InboundUseCaseHistoryEntry",
            "outbound": "#/components/schemas/OutboundUseCaseHistoryEntry",
            "file_proxy": "#/components/schemas/FileProxyUseCaseHistoryEntry",
            "managed_call": "#/components/schemas/ManagedCallUseCaseHistoryEntry",
            "secure_proxy": "#/components/schemas/SecureProxyUseCaseHistoryEntry"
          }
        }
      },
      "UseCaseHistoryEntryBase": {
        "type": "object",
        "required": [
          "id",
          "useCaseId",
          "integrationId",
          "name",
          "type",
          "enabled",
          "created_at",
          "updated_at",
          "history_created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for this history entry"
          },
          "useCaseId": {
            "type": "string",
            "format": "uuid",
            "description": "Reference to the parent use case"
          },
          "integrationId": {
            "type": "string",
            "format": "uuid",
            "description": "Parent integration ID"
          },
          "name": {
            "type": "string",
            "description": "Use case name at this point in history"
          },
          "slug": {
            "type": "string",
            "description": "Use case slug at this point in history"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the use case was enabled at this point in history"
          },
          "change_description": {
            "type": "string",
            "description": "Description of the change that was made at this point in history"
          },
          "changed_by": {
            "type": "string",
            "description": "User ID of the user who made the change that produced this history entry"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO-8601 timestamp when the use case was originally created"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO-8601 timestamp of this historical snapshot (before the update)"
          },
          "history_created_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO-8601 timestamp when this history entry was created"
          }
        }
      },
      "InboundUseCaseHistoryEntry": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UseCaseHistoryEntryBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "inbound"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/InboundIntegrationEventConfiguration"
              }
            }
          }
        ]
      },
      "OutboundUseCaseHistoryEntry": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UseCaseHistoryEntryBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "outbound"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/OutboundIntegrationEventConfiguration"
              }
            }
          }
        ]
      },
      "FileProxyUseCaseHistoryEntry": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UseCaseHistoryEntryBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "file_proxy"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/FileProxyUseCaseConfiguration"
              }
            }
          }
        ]
      },
      "ManagedCallUseCaseHistoryEntry": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UseCaseHistoryEntryBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "managed_call"
                ],
                "description": "Use case type for managed API calls"
              },
              "configuration": {
                "$ref": "#/components/schemas/ManagedCallOperationConfig"
              },
              "type_annotations": {
                "$ref": "#/components/schemas/TypeAnnotations"
              },
              "types_locked": {
                "type": "boolean",
                "description": "Whether types have been generated for this use case"
              }
            }
          }
        ]
      },
      "SecureProxyUseCaseHistoryEntry": {
        "allOf": [
          {
            "$ref": "#/components/schemas/UseCaseHistoryEntryBase"
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "secure_proxy"
                ],
                "description": "Use case type"
              },
              "configuration": {
                "$ref": "#/components/schemas/SecureProxyUseCaseConfiguration"
              }
            }
          }
        ]
      },
      "SecureProxyUseCaseConfiguration": {
        "type": "object",
        "required": [
          "vpc_mode"
        ],
        "description": "Configuration for secure_proxy use cases. Defines how to route requests through a secure VPC.\n",
        "properties": {
          "vpc_mode": {
            "type": "string",
            "enum": [
              "static_ip",
              "secure_link"
            ],
            "readOnly": true,
            "description": "VPC routing mode. Read-only after creation.\n- static_ip: Routes through a VPC with static outbound IP (NAT Gateway) for IP-allowlisted external APIs.\n- secure_link: Routes through a VPN VPC for accessing private customer networks.\n"
          },
          "allowed_domains": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "readOnly": true,
            "description": "Domain whitelist for secure_link mode. Admin-only — can only be modified directly in DynamoDB via admin script.\nSupports exact match (e.g., \"api.wemag.com\") and wildcard prefix (e.g., \"*.wemag.com\").\n"
          },
          "allowed_ips": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "readOnly": true,
            "description": "IP allowlist (CIDR notation) for secure_link mode. Admin-only — can only be modified directly in DynamoDB via admin script.\nRequired for secure_link mode. All DNS-resolved IPs must match at least one range.\nExample: [\"10.0.1.0/24\", \"192.168.1.0/24\"]\n"
          }
        }
      },
      "SecureProxyWhitelist": {
        "type": "object",
        "required": [
          "vpc_mode",
          "allowed_domains",
          "allowed_ips"
        ],
        "description": "Current whitelist state for a secure_proxy use case. vpc_mode is read-only\ncontext so the UI can show the user what mode the pool is in.\n",
        "properties": {
          "vpc_mode": {
            "type": "string",
            "nullable": true,
            "enum": [
              "static_ip",
              "secure_link"
            ],
            "description": "VPC routing mode (read-only). `null` if not yet set."
          },
          "allowed_domains": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Exact domain (\"api.example.com\") or wildcard prefix (\"*.example.com\").\nWildcards must have at least 2 labels in the suffix.\n"
          },
          "allowed_ips": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "CIDR-notation IP ranges, e.g. \"10.0.0.0/24\"."
          }
        }
      },
      "SecureProxyWhitelistUpdate": {
        "type": "object",
        "description": "Partial update for a secure_proxy whitelist. At least one of\n`allowed_domains` or `allowed_ips` must be provided.\n\nPer-field semantics:\n  - **omitted** — the field is not modified; the stored value is preserved.\n  - **non-empty array** — the stored value is replaced with the supplied list.\n  - **empty array (`[]`)** — the list is cleared (stored as `[]`). This is the\n    canonical way to remove all entries from a list. `null` is not accepted.\n",
        "minProperties": 1,
        "properties": {
          "allowed_domains": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Exact domain (\"api.example.com\") or wildcard prefix (\"*.example.com\").\nWildcards must have at least 2 labels in the suffix.\nPass `[]` to clear all allowed domains.\n"
          },
          "allowed_ips": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "CIDR-notation IP ranges, e.g. \"10.0.0.0/24\".\nPass `[]` to clear all allowed IPs.\n"
          }
        }
      },
      "SecureProxySummary": {
        "type": "object",
        "required": [
          "id",
          "name",
          "enabled",
          "vpc_mode",
          "integration_id",
          "integration_name"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "vpc_mode": {
            "type": "string",
            "enum": [
              "static_ip",
              "secure_link"
            ]
          },
          "allowed_domains": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "allowed_ips": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "integration_id": {
            "type": "string",
            "format": "uuid"
          },
          "integration_name": {
            "type": "string"
          }
        }
      },
      "SecureProxyRequest": {
        "type": "object",
        "required": [
          "url",
          "method",
          "integration_id"
        ],
        "properties": {
          "integration_id": {
            "type": "string",
            "format": "uuid",
            "description": "Integration ID that owns the secure_proxy use case"
          },
          "use_case_id": {
            "type": "string",
            "format": "uuid",
            "description": "Use case ID (provide either use_case_id or use_case_slug)"
          },
          "use_case_slug": {
            "type": "string",
            "description": "Use case slug (provide either use_case_id or use_case_slug)"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Target URL to proxy the request to"
          },
          "method": {
            "type": "string",
            "enum": [
              "GET",
              "POST",
              "PUT",
              "PATCH",
              "DELETE"
            ],
            "description": "HTTP method"
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Request headers to forward"
          },
          "body": {
            "description": "Request body (string or JSON)"
          },
          "response_type": {
            "type": "string",
            "enum": [
              "json",
              "binary"
            ],
            "default": "json",
            "description": "Expected response type"
          }
        }
      },
      "SecureProxyResponse": {
        "type": "object",
        "properties": {
          "status_code": {
            "type": "integer",
            "description": "HTTP status code from the upstream response"
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Response headers from upstream"
          },
          "body": {
            "description": "Response body from upstream. When `status_code` is 502 and the target never produced an HTTP response (TLS/connection/DNS failure or timeout), this is a `SecureProxyUpstreamError` describing the underlying cause."
          }
        }
      },
      "SecureProxyUpstreamError": {
        "type": "object",
        "description": "Error payload returned when epilot could not obtain an HTTP response from the proxied target. The failure is epilot-generated (HTTP 502) but the cause is usually remote-side; `code`/`reason` make that attributable.",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string",
            "description": "Short error category.",
            "enum": [
              "Upstream network error",
              "Upstream error"
            ]
          },
          "code": {
            "type": "string",
            "description": "Underlying Node.js/axios error code when available (e.g. `UNABLE_TO_VERIFY_LEAF_SIGNATURE`, `ECONNREFUSED`, `ETIMEDOUT`, `ENOTFOUND`)."
          },
          "reason": {
            "type": "string",
            "description": "Human-readable explanation, present only for well-known codes (TLS/certificate, DNS and connection failures)."
          }
        }
      },
      "ManagedCallOperationConfig": {
        "type": "object",
        "required": [
          "operation"
        ],
        "description": "Configuration for managed_call use cases. Defines a single API operation with JSONata mapping.",
        "properties": {
          "operation": {
            "$ref": "#/components/schemas/ManagedCallOperation"
          },
          "request_mapping": {
            "type": "string",
            "description": "JSONata expression for outbound body transformation"
          },
          "response_mapping": {
            "type": "string",
            "description": "JSONata expression for inbound response transformation"
          },
          "inbound_use_case_slug": {
            "type": "string",
            "description": "Slug of the inbound use case to route responses to for async entity processing.\nWhen set, the managed call response is queued to the inbound pipeline and processed\nusing the referenced inbound use case's mapping configuration.\n"
          }
        }
      },
      "ManagedCallOperation": {
        "type": "object",
        "required": [
          "method",
          "path"
        ],
        "description": "HTTP operation configuration for managed calls",
        "properties": {
          "method": {
            "type": "string",
            "enum": [
              "GET",
              "POST",
              "PUT",
              "PATCH",
              "DELETE"
            ]
          },
          "path": {
            "type": "string",
            "description": "URL path template with {{variable}} interpolation"
          },
          "headers": {
            "type": "object",
            "description": "Custom HTTP headers for the request. Values support {{variable}} interpolation from the request payload and {{env.VAR}} references for environment variables.",
            "additionalProperties": {
              "type": "string"
            }
          },
          "query_params": {
            "type": "object",
            "description": "Query parameters for the request. Values support {{variable}} interpolation from the request payload.",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "ManagedCallExecuteRequest": {
        "type": "object",
        "required": [
          "integration_id"
        ],
        "properties": {
          "integration_id": {
            "type": "string",
            "format": "uuid",
            "description": "Integration ID"
          },
          "payload": {
            "type": "object",
            "additionalProperties": true,
            "description": "Request payload for the managed call operation"
          },
          "correlation_id": {
            "type": "string",
            "description": "Correlation ID for tracing related events (auto-generated if not provided)"
          }
        }
      },
      "ManagedCallExecuteResponse": {
        "description": "The response from a managed call execution.\nOn success, returns the JSONata-mapped response data directly (no wrapper).\nThe shape is entirely defined by your response_mapping JSONata expression.\nIf no response_mapping is configured, returns the raw external API response.\nCheck the X-Inbound-Event-Id header for inbound pipeline tracking when inbound routing is configured.\n",
        "additionalProperties": true
      },
      "ManagedCallErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Error code (e.g., EXTERNAL_API_ERROR, SSRF_BLOCKED, MANAGED_CALL_EXECUTION_ERROR)"
              },
              "message": {
                "type": "string",
                "description": "Human-readable error message"
              },
              "details": {
                "type": "object",
                "additionalProperties": true,
                "description": "Additional error details (e.g., status code and body for external API errors)"
              }
            }
          }
        }
      },
      "TypeAnnotations": {
        "type": "object",
        "description": "Developer-provided type annotations for a use case's request and response fields",
        "properties": {
          "request": {
            "type": "object",
            "description": "Type annotations for request fields, keyed by dot-path (e.g., \"vendors[].id\" -> \"string\")",
            "additionalProperties": {
              "type": "string"
            }
          },
          "response": {
            "type": "object",
            "description": "Type annotations for response fields",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "TypeDescriptor": {
        "type": "object",
        "description": "Describes the inferred type shape of a JSONata expression",
        "required": [
          "kind"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "object",
              "array",
              "string",
              "number",
              "boolean",
              "null",
              "unknown",
              "union"
            ]
          },
          "properties": {
            "type": "object",
            "description": "For kind=object, the properties and their type descriptors",
            "additionalProperties": {
              "$ref": "#/components/schemas/TypeDescriptor"
            }
          },
          "items": {
            "description": "For kind=array, the type of array items",
            "allOf": [
              {
                "$ref": "#/components/schemas/TypeDescriptor"
              }
            ]
          },
          "source": {
            "type": "string",
            "description": "For kind=unknown, the JSONata path that produced this value"
          },
          "variants": {
            "type": "array",
            "description": "For kind=union, the variant types",
            "items": {
              "$ref": "#/components/schemas/TypeDescriptor"
            }
          }
        }
      },
      "UseCaseTypePreview": {
        "type": "object",
        "description": "Scaffolded type descriptors for a single use case",
        "required": [
          "slug",
          "request_shape",
          "response_shape"
        ],
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "request_shape": {
            "$ref": "#/components/schemas/TypeDescriptor"
          },
          "response_shape": {
            "$ref": "#/components/schemas/TypeDescriptor"
          },
          "existing_annotations": {
            "$ref": "#/components/schemas/TypeAnnotations"
          }
        }
      },
      "GenerateTypesPreviewResponse": {
        "type": "object",
        "required": [
          "use_cases"
        ],
        "properties": {
          "integration_name": {
            "type": "string"
          },
          "use_cases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UseCaseTypePreview"
            }
          },
          "previous_version": {
            "type": "string",
            "description": "Previously generated version, if any"
          },
          "suggested_version": {
            "type": "string",
            "description": "Suggested next version based on detected changes"
          },
          "suggested_bump": {
            "type": "string",
            "enum": [
              "major",
              "minor"
            ]
          },
          "detected_changes": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "slug",
                "field",
                "type",
                "description"
              ],
              "properties": {
                "slug": {
                  "type": "string"
                },
                "field": {
                  "type": "string"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "breaking",
                    "non-breaking"
                  ]
                },
                "description": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "GenerateTypesRequest": {
        "type": "object",
        "required": [
          "package_name",
          "version"
        ],
        "properties": {
          "package_name": {
            "type": "string",
            "description": "npm package name",
            "example": "@epilot/hems-cleverpv"
          },
          "version": {
            "type": "string",
            "description": "Package version",
            "example": "1.0.0"
          },
          "description": {
            "type": "string"
          },
          "domain_package": {
            "type": "string",
            "description": "Domain standard package to extend",
            "example": "@epilot/hems"
          },
          "domain_map_name": {
            "type": "string",
            "description": "Name of the domain UseCaseMap to extend",
            "example": "HemsUseCaseMap"
          },
          "annotations": {
            "type": "object",
            "description": "Type annotations per use case slug",
            "additionalProperties": {
              "$ref": "#/components/schemas/TypeAnnotations"
            }
          }
        }
      },
      "GenerateTypesResponse": {
        "type": "object",
        "required": [
          "package_name",
          "version",
          "files"
        ],
        "properties": {
          "package_name": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "files": {
            "type": "object",
            "description": "Generated file contents keyed by file path",
            "additionalProperties": {
              "type": "string"
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CommitTypesRequest": {
        "type": "object",
        "required": [
          "package_name",
          "version"
        ],
        "properties": {
          "package_name": {
            "type": "string",
            "description": "npm package name",
            "example": "@epilot/hems-cleverpv"
          },
          "version": {
            "type": "string",
            "description": "Package version",
            "example": "1.0.0"
          },
          "annotations": {
            "type": "object",
            "description": "Type annotations per use case slug",
            "additionalProperties": {
              "$ref": "#/components/schemas/TypeAnnotations"
            }
          }
        }
      },
      "CommitTypesResponse": {
        "type": "object",
        "required": [
          "committed"
        ],
        "properties": {
          "committed": {
            "type": "boolean"
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "FileProxyUseCaseConfiguration": {
        "type": "object",
        "required": [
          "steps"
        ],
        "description": "Configuration for file_proxy use cases. Defines how to authenticate and move files\nbetween epilot and an external document system, in either direction (see `direction`).\n\n**Download** (`direction: download`, the default) fetches a file from the external system\nand serves it to a browser. The download URL always requires `orgId`, `integrationId`, and\neither `useCaseSlug` (recommended) or `useCaseId` (legacy UUID) as query parameters.\nThe `orgId` is included in the signed URL to establish organization context without\nrequiring authentication. Additional use-case-specific parameters are declared in the\n`params` array. `response` is REQUIRED for download use cases.\n\n**Upload** (`direction: upload`) pushes epilot files to the external system. It is not\nreachable over the download endpoint; an outbound use case points at it via a `file_proxy`\ndelivery, and this configuration owns everything about what gets sent: `fan_out` decides\nhow many deliveries one event produces, and every step builds its own request body with\n`body_jsonata` — or leaves it empty to send the delivery's files unchanged.\n`upload` is REQUIRED and `response` MUST be omitted.\n\nTwo expression languages, split by what they produce. **Handlebars composes strings**:\n`url` and `headers` on upload, plus a form-encoded, XML or plain-text `body` on\ndownload. **JSONata produces data**: `body_jsonata` and the per-step `enabled`.\nJSONata omits keys whose value is undefined, so an optional field needs no conditional\nguard — it is simply absent from the serialized body.\n\nHandlebars templates are rendered EXACTLY ONCE, against a single context holding `env`,\n`file_data`, `steps` and `auth_token`. Writing\n`{{ env.some_var }}` resolves it. The legacy `\\{{ env.some_var }}` escape belongs to the\ntwo-pass renderer and is NOT rewritten here — it renders as the literal text\n`{{ env.some_var }}`, which is rejected (see below) rather than shipped.\n\nTwo guards run on every rendered upload template, because single-pass rendering fails\nquietly by default. Both are terminal, and each names what to fix:\n\n- **residual `{{` after rendering** — a configuration still carrying the `\\{{` escape.\n  Rewrite it without the backslash.\n- **a referenced `env` key absent from the environment** — checked BEFORE the URL is\n  parsed, because an empty value in host position turns\n  `https://{{env.host}}/document/import` into `https:///document/import`, whose host\n  then parses as `document`. Provision the environment variable. A key that exists and\n  is legitimately empty is fine; only absence fails.\n\nOpenAPI 3.0 cannot express this conditional requiredness, so it is enforced by the\nserver-side validator, which returns an explicit message naming the offending field.\n",
        "properties": {
          "direction": {
            "type": "string",
            "enum": [
              "download",
              "upload"
            ],
            "default": "download",
            "description": "Direction of file travel. `download` (default) pulls a file from the external system\ninto epilot; `upload` pushes an epilot file out to the external system. Omitted means\n`download`, so every pre-existing configuration keeps its exact meaning.\n\nNote this is the direction of the FILE, not the epilot use-case type — an `upload`\nfile_proxy use case is still a `file_proxy` use case, never an `outbound` one.\n"
          },
          "upload": {
            "$ref": "#/components/schemas/FileProxyUploadConfig"
          },
          "fan_out": {
            "$ref": "#/components/schemas/FileProxyFanOutConfig"
          },
          "secure_proxy": {
            "description": "Optional secure proxy attachment for routing all outbound file proxy requests.\nOnly `use_case_slug` is supported and the referenced secure_proxy use case\nmust belong to the same integration.\n",
            "allOf": [
              {
                "$ref": "#/components/schemas/FileProxySecureProxyAttachment"
              }
            ]
          },
          "auth": {
            "$ref": "#/components/schemas/FileProxyAuth"
          },
          "params": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FileProxyParam"
            },
            "description": "Download-only. Additional use-case-specific parameters expected in the download URL\nquery string (beyond the required orgId, integrationId, and useCaseSlug or useCaseId).\nRejected when `direction` is `upload`.\n"
          },
          "allowed_origins": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri",
              "pattern": "^https?://"
            },
            "description": "Download-only. Additional origins permitted to call /download for this use case\n(CORS, exact match). Portal origins are always allowed. Rejected when `direction`\nis `upload`.\n"
          },
          "steps": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/FileProxyStep"
            },
            "description": "Ordered list of HTTP steps to execute. For `download` these retrieve the file; for\n`upload` they deliver it, each assembling its own request body — `body_jsonata` for\nJSON, `body` for anything else.\n"
          },
          "response": {
            "$ref": "#/components/schemas/FileProxyResponseConfig"
          },
          "prevent_indirect_serving": {
            "type": "boolean",
            "default": false,
            "description": "Download-only; rejected when `direction` is `upload`.\nWhen `true`, this use case is served via the streaming endpoint: mapped file URLs\nare built as `/stream/download`, files of any size are streamed inline over HTTP\nresponse streaming, and buffered `/download` requests for oversize files are\n307-redirected to `/stream`. Files never transit epilot's temporary S3 storage on\nthe streaming path. Defaults to `false` (small files are served directly and large\nfiles are transparently served via a temporary S3 redirect).\n"
          }
        }
      },
      "FileProxySecureProxyAttachment": {
        "type": "object",
        "required": [
          "use_case_slug"
        ],
        "additionalProperties": false,
        "properties": {
          "use_case_slug": {
            "type": "string",
            "description": "Slug of the secure_proxy use case in the same integration."
          }
        }
      },
      "FileProxyAuth": {
        "type": "object",
        "required": [
          "type",
          "token_url",
          "client_id",
          "client_secret"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "oauth2_client_credentials",
              "oauth2_password"
            ],
            "description": "Authentication type"
          },
          "token_url": {
            "type": "string",
            "description": "Handlebars template for the OAuth2 token endpoint URL"
          },
          "client_id": {
            "type": "string",
            "description": "Handlebars template for the OAuth2 client ID"
          },
          "client_secret": {
            "type": "string",
            "description": "Handlebars template for the OAuth2 client secret"
          },
          "scope": {
            "type": "string",
            "description": "Optional OAuth2 scope"
          },
          "audience": {
            "type": "string",
            "description": "Handlebars template for the OAuth2 audience parameter"
          },
          "resource": {
            "type": "string",
            "description": "Handlebars template for the OAuth2 resource parameter"
          },
          "username": {
            "type": "string",
            "description": "Handlebars template for the OAuth2 resource owner username. Required when type is oauth2_password."
          },
          "password": {
            "type": "string",
            "description": "Handlebars template for the OAuth2 resource owner password. Required when type is oauth2_password."
          },
          "body_params": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Additional key-value pairs to include in the token request body. Values support Handlebars templates."
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Additional headers to include in the token request. Values support Handlebars templates."
          },
          "query_params": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Additional query parameters to append to the token URL. Values support Handlebars templates."
          }
        }
      },
      "FileProxyParam": {
        "type": "object",
        "required": [
          "name",
          "required"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Parameter name as it appears in the query string"
          },
          "required": {
            "type": "boolean",
            "description": "Whether this parameter is required"
          },
          "description": {
            "type": "string",
            "description": "Human-readable description of the parameter"
          }
        }
      },
      "FileProxyStep": {
        "type": "object",
        "required": [
          "url",
          "method",
          "response_type"
        ],
        "properties": {
          "url": {
            "type": "string",
            "description": "Handlebars template for the request URL"
          },
          "method": {
            "type": "string",
            "enum": [
              "GET",
              "POST",
              "PUT",
              "PATCH"
            ],
            "description": "HTTP method"
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Handlebars templates for request headers"
          },
          "body_jsonata": {
            "type": "string",
            "description": "Upload-only. JSONata producing the request body as DATA; the result is serialized to\nJSON and sent. Use this for every JSON body — it cannot emit malformed JSON, and it\nomits a key whose value is undefined instead of sending it empty, which is what makes\noptional fields work without a conditional guard.\n\nThe evaluation root is the hydrated event, so `contact.customer_pin` and\n`ticket._purpose` are reachable directly, unprefixed. **Everything else is a\n`$`-prefixed binding**: `$file_data` (the files this delivery carries — see\n`FileProxyFanOutConfig` for what it holds in each mode), `$ack_id`, `$env`,\n`$steps` (results of the steps already executed, each\n`{statusCode, headers, body}`), `$germanDate(iso)` and `$now()`.\nWriting `file_data[0].filename` instead of `$file_data[0].filename` yields\nnothing — it reads a field named `file_data` on the event, which does not exist.\n\nLeave this empty to send the delivery's files exactly as they are: the single\nattachment object when fanning out, the whole array when not. No mapping is\nneeded for the common case.\n\nMust evaluate to an object or an array. Anything else fails the delivery terminally\nwith `MAPPING_EXPRESSION_FAILED`, naming the step.\n\n**Only `undefined` omits a key.** `null`, `\"\"`, `false` and `0` are values and are\nall sent. Two traps follow from that, and one idiom avoids both:\n\n- `x ? $string(x)` omits the key when `x` is `0` or `\"\"`, because JSONata reads both\n  as false. A meter number of `\"0\"` would silently vanish.\n- `: undefined` is not a literal — JSONata has no `undefined` keyword, so it is a\n  path lookup that happens to find nothing. Against an event that really does carry\n  a field named `undefined`, it returns that field's value.\n\nWrite the **two-arm ternary with no else branch**, which omits the key when the test\nis false: `{ \"pin\": $exists(contact.pin) ? $string(contact.pin) }`.\n\nEvery `$` binding is checked at save time against the known set, so a mistyped\nbinding (`$lookup.documentType` for `$file_data[0].filename`) is rejected rather\nthan silently producing a missing key.\n\nNot accepted together with `body`, which the upload direction rejects outright.\n",
            "example": "{ \"documentType\": \"Zählerstand\", \"fileName\": $file_data[0].filename, \"fileData\": $file_data[0].base64, \"pin\": $exists(contact.customer_pin) ? $string(contact.customer_pin) }"
          },
          "enabled": {
            "type": "string",
            "description": "Upload-only, OPTIONAL. JSONata returning a boolean, deciding whether this step\nruns at all. Absent means it runs.\n\nA false result is a BREAK: this step is skipped and so is every step after it,\nand the delivery is recorded as `skipped` rather than delivered or failed. It is\nacknowledged and never retried, and a `STEP_DISABLED` monitoring event is emitted\nat level `info` — a disabled step is the configuration working, not a fault.\n\nReads the same bindings a body does, `$steps` included, so it can branch on what\nan earlier step returned. This is how a delivery is filtered out: with one\ndelivery per attachment, a false result on the first step drops that file.\n\nAn expression that throws, or returns a non-boolean, is a terminal\n`MAPPING_EXPRESSION_FAILED` instead — a broken predicate must not read as a\ndeliberate skip.\n",
            "example": "$file_data[0].mime_type = \"application/pdf\""
          },
          "body": {
            "type": "string",
            "description": "Handlebars template for the request body (write methods only), for bodies that are\nNOT JSON — form-encoded, XML, plain text. For a JSON body use `body_jsonata`\ninstead; it cannot produce malformed JSON.\n\n**Download-only.** The upload direction rejects it: use `body_jsonata`, or leave\nthat empty to send the files unchanged.\n\nRendered once, against the context described on `FileProxyUseCaseConfiguration`:\n`{{ params.* }}`, `{{ env.* }}`, `{{ steps.N.body }}`, `{{ auth_token }}`.\n\nIf a JSON body is written here anyway, **route every user-controlled value through\nthe `json` helper**: the template engine does not escape, so\n`\"name\":\"{{ params.documentName }}\"` produces invalid JSON the moment a value\ncontains a quote. Write `\"name\": {{json params.documentName}}` instead — the helper\nemits the surrounding quotes itself and renders absent values as `null`.\n\nMutually exclusive with `body_jsonata`, which is upload-only.\n"
          },
          "response_type": {
            "type": "string",
            "enum": [
              "json",
              "binary"
            ],
            "description": "Expected response type"
          }
        }
      },
      "FileProxyFanOutConfig": {
        "type": "object",
        "required": [
          "enabled"
        ],
        "description": "Whether one event produces one delivery per file, or a single delivery carrying all of\nthem. The split is always over the event's `event_attachments` — there is no expression\nto write, because an upload only ever runs on events that declare that field.\n\nEach resulting delivery is fully independent: its own idempotency record, its own retry\nschedule, its own monitoring events. A four-file event can therefore end up three-of-four\ndelivered, which is the honest state to report.\n\nThe split is evaluated ONCE, when the event is enqueued, so item indices — and therefore\nidempotency keys — stay stable across retries.\n\nEither way, expressions read the same `$file_data` binding, so no expression needs to\nknow which mode it runs in. An event carrying no attachments is recorded as `skipped`.\n",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "When true, one delivery per attachment, and `$file_data` holds exactly that one\nfile. When false (or absent), the event produces exactly one delivery and\n`$file_data` holds every attachment on the event.\n\nAn empty `event_attachments` produces no deliveries and one info-level\n`FAN_OUT_EMPTY`, which is the normal outcome for a catch-all subscription seeing an\nevent with nothing to send.\n"
          }
        }
      },
      "FileProxyUploadConfig": {
        "type": "object",
        "description": "Upload-side settings for a file_proxy use case with `direction: upload`.\nThe surrounding file_proxy configuration owns WHAT and HOW to send: `fan_out`,\nauth, and the steps with their `body_jsonata`. This nested object governs\nupload-specific limits and how the final external response is judged. The\noutbound mapping remains a pure pointer to the recipe (see `FileProxyDeliveryConfig`).\n",
        "properties": {
          "max_total_bytes": {
            "type": "integer",
            "description": "Ceiling for ALL of a delivery's files together, in bytes. Capped by the platform\nlimit, which it may lower but never raise.\n\nOnly reachable with `fan_out.enabled: false`, where one delivery carries every\nattachment and the worker holds them all resident at once. base64 inflates each\nby about a third, so the combined figure is what matters rather than any single\nfile's size.\n",
            "example": 26214400
          },
          "max_file_bytes": {
            "type": "integer",
            "minimum": 1,
            "maximum": 104857600,
            "description": "Per-file ceiling for this use case, in bytes. Files above it fail terminally with\n`FILE_TOO_LARGE` before any bytes are fetched. Defaults to — and is clamped by — the\nplatform ceiling of 100 MiB (104857600).\n"
          },
          "max_delivery_attempts": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 8,
            "description": "Maximum delivery attempts per item before the delivery is marked failed. Attempts\nare spaced by an exponential, jittered backoff. The default 8 attempts schedule at\nmost 7 delays, totaling about 7 hours 40 minutes before jitter, so a normal ERP\nmaintenance window does not immediately exhaust them.\n\nLives here rather than on the outbound delivery because how hard to retry is a\nproperty of the transport — the same judgement as `max_file_bytes` — and the outbound\ndelivery is a pure pointer.\n"
          }
        }
      },
      "FileProxyResponseConfig": {
        "type": "object",
        "description": "How to extract the file from the step results. REQUIRED when `direction` is `download`;\nrejected when `direction` is `upload` (an upload has no file to extract).\n",
        "required": [
          "body",
          "encoding"
        ],
        "properties": {
          "body": {
            "type": "string",
            "description": "JSONata expression to extract file content from step results"
          },
          "encoding": {
            "type": "string",
            "enum": [
              "base64",
              "binary"
            ],
            "description": "Encoding of the extracted body"
          },
          "filename": {
            "type": "string",
            "description": "JSONata expression to extract the filename"
          },
          "content_type": {
            "type": "string",
            "description": "JSONata expression to extract the content type"
          }
        }
      },
      "MappingSimulationRequest": {
        "type": "object",
        "required": [
          "mapping_configuration",
          "object_type",
          "format",
          "payload"
        ],
        "properties": {
          "mapping_configuration": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/IntegrationConfigurationV1"
              },
              {
                "$ref": "#/components/schemas/IntegrationConfigurationV2"
              }
            ]
          },
          "object_type": {
            "type": "string",
            "description": "Type of the object/event being mapped.\nFor v1.0: must match a key in mapping_configuration.mapping.objects\nFor v2.0: must match a key in mapping_configuration.mapping.events\n"
          },
          "format": {
            "type": "string",
            "enum": [
              "json",
              "xml"
            ],
            "default": "json",
            "description": "Format of the payload data"
          },
          "payload": {
            "oneOf": [
              {
                "type": "string",
                "description": "The serialized object data payload (JSON, XML, etc.) as a string"
              },
              {
                "type": "object",
                "description": "Direct JSON object (will be automatically serialized)",
                "additionalProperties": true
              }
            ],
            "description": "The object data payload - can be either a serialized string or a direct JSON object"
          }
        }
      },
      "MappingSimulationV2Request": {
        "type": "object",
        "description": "Request for v2 mapping simulation. Uses the same configuration format stored in integration use case resources,\nmaking it easier to test configurations before saving them.\n",
        "required": [
          "event_configuration",
          "format",
          "payload"
        ],
        "properties": {
          "event_configuration": {
            "$ref": "#/components/schemas/InboundIntegrationEventConfiguration"
          },
          "format": {
            "type": "string",
            "enum": [
              "json",
              "xml"
            ],
            "default": "json",
            "description": "Format of the payload data"
          },
          "payload": {
            "oneOf": [
              {
                "type": "string",
                "description": "The serialized event data payload (JSON, XML, etc.) as a string"
              },
              {
                "type": "object",
                "description": "Direct JSON object (will be automatically serialized)",
                "additionalProperties": true
              }
            ],
            "description": "The event data payload - can be either a serialized string or a direct JSON object"
          }
        }
      },
      "MappingSimulationResponse": {
        "type": "object",
        "required": [
          "entity_updates"
        ],
        "properties": {
          "entity_updates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EntityUpdate"
            }
          },
          "meter_readings_updates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MeterReadingUpdate"
            }
          },
          "prune_scope_updates": {
            "type": "array",
            "description": "Resolved prune scopes produced by entity mappings using `upsert-prune-scope-purge` / `upsert-prune-scope-delete`. Each entry describes the entities that would be deleted at runtime because they exist in scope but were not part of this payload.",
            "items": {
              "$ref": "#/components/schemas/EntityPruneScopeUpdate"
            }
          },
          "meter_readings_prune_scope_updates": {
            "type": "array",
            "description": "Resolved prune scopes produced by meter reading mappings using `upsert-prune-scope`. Each entry describes the readings that would be deleted at runtime for a meter (+ counter) because they were not part of this payload.",
            "items": {
              "$ref": "#/components/schemas/MeterReadingPruneScopeUpdate"
            }
          },
          "warnings": {
            "type": "array",
            "description": "Validation warnings about the configuration (e.g., unique_ids referencing non-indexed fields)",
            "items": {
              "$ref": "#/components/schemas/MappingSimulationWarning"
            }
          }
        }
      },
      "MappingSimulationWarning": {
        "type": "object",
        "required": [
          "entity_schema",
          "field",
          "message"
        ],
        "properties": {
          "entity_schema": {
            "type": "string",
            "description": "The entity schema slug where the issue was found"
          },
          "field": {
            "type": "string",
            "description": "The field name that caused the warning"
          },
          "message": {
            "type": "string",
            "description": "Description of the validation issue"
          }
        }
      },
      "DirectPayload": {
        "type": "object",
        "description": "The payload of an ERP event routed to a `direct: true` use case: a versioned envelope of\npre-mapped operations that skip the mapping engine. Sent as the `payload` of an\n`ErpEventV3` - either as a JSON object or as a JSON string (`format` must be `json`;\nXML is rejected for direct use cases).\n\nThe schema is strict: unknown keys on the envelope or on any operation are rejected with\nan error naming the offending path. Attribute values, by contrast, pass through to the\nentity verbatim (except `$relation` / `$relation_ref` envelopes, which are validated and\nresolved).\n\nOne event lands as one queue message, so the whole serialized event (including this\npayload and its internal envelope) must stay within the 1 MiB message budget. Identical\nconsecutive payloads within 5 minutes can be deduplicated by the queue - use the event's\n`deduplication_id` deliberately when re-sending identical data.\n",
        "required": [
          "version",
          "operations"
        ],
        "properties": {
          "version": {
            "type": "string",
            "description": "Version of the direct payload contract. Currently only `\"1\"` is supported; an\nunsupported version is rejected with `DIRECT_VERSION_UNSUPPORTED`. Deliberately not\nan enum: the constraint is enforced by the handler so the dry-run endpoint returns\na structured `valid: false` verdict instead of a schema-level 400.\n"
          },
          "operations": {
            "type": "array",
            "description": "The operations to apply, in order. At most 100 operations per event (enforced by\nthe handler, with a structured error naming the limit). Each item is either an\nentity operation (`type` omitted or `\"entity\"`) or a meter reading operation\n(`type: \"meter_reading\"`).\n",
            "items": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/DirectEntityOperation"
                },
                {
                  "$ref": "#/components/schemas/DirectMeterReadingOperation"
                }
              ]
            }
          }
        }
      },
      "DirectEntityOperation": {
        "type": "object",
        "description": "A single pre-mapped entity operation: upserts, soft-deletes, or purges one entity,\nidentified by its unique ids. Produces the same internal update as a mapped-mode entity\nconfiguration, so create-vs-update resolution, relation resolution, ordering, and\nmonitoring behave identically.\n",
        "required": [
          "entity_slug",
          "unique_ids"
        ],
        "additionalProperties": false,
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "entity"
            ],
            "description": "Operation discriminator. Optional - an operation without `type` is an entity operation."
          },
          "entity_slug": {
            "type": "string",
            "minLength": 1,
            "description": "Target entity schema slug (e.g. `contact`, `contract`).",
            "example": "contract"
          },
          "unique_ids": {
            "type": "object",
            "minProperties": 1,
            "additionalProperties": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                }
              ]
            },
            "description": "Attribute values that uniquely identify this entity, as `{ attribute: value }`.\nValues may be strings or numbers (numbers are coerced to strings); values that are\nempty after trimming are rejected. Use `_id` as the sole key to reference an entity\ndirectly by its epilot id.\n\nUnlike mapped mode, the unique-id ATTRIBUTE NAMES are given directly - there is no\nfield-to-attribute mapping step.\n",
            "example": {
              "contract_number": "CTR-001"
            }
          },
          "unique_id_types": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "enum": [
                "email",
                "phone"
              ]
            },
            "description": "Optional per-field type hints for repeatable unique-id fields (`email` / `phone`),\nneeded so lookups search the repeatable storage format correctly. When omitted, the\nserver derives the types from the entity schema; explicit values always win over\nderivation.\n"
          },
          "mode": {
            "type": "string",
            "enum": [
              "upsert",
              "delete",
              "purge"
            ],
            "default": "upsert",
            "description": "Operation mode: `upsert` creates or updates the entity (default), `delete` soft\ndeletes it, `purge` removes it permanently. For `delete` / `purge`, `attributes`\nmay be omitted - only `unique_ids` are used to locate the entity.\n"
          },
          "attributes": {
            "type": "object",
            "description": "Attribute values to write, keyed by attribute name. Required when `mode` is `upsert`\n(an empty object is allowed); optional for `delete` / `purge`.\n\nValues are written verbatim - the server does not validate them against the entity\nschema (attributes not defined in the schema are stored but not indexed; the\nsimulation endpoint surfaces warnings for unique-id fields missing from the schema).\nTwo envelope forms receive special treatment: `$relation` values\n(`DirectRelationValue`) and `$relation_ref` values (`DirectRelationRefValue`) are\nvalidated and resolved to entity relations.\n",
            "additionalProperties": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/DirectRelationValue"
                },
                {
                  "$ref": "#/components/schemas/DirectRelationRefValue"
                },
                {
                  "description": "Any other JSON value - written to the entity attribute verbatim."
                }
              ]
            }
          }
        }
      },
      "DirectMeterReadingOperation": {
        "type": "object",
        "description": "A single pre-mapped meter reading operation: upserts or deletes one reading on a meter\n(and optionally a specific counter), identified by their unique ids. Produces the same\ninternal update as a mapped-mode meter reading configuration.\n",
        "required": [
          "type",
          "meter",
          "attributes"
        ],
        "additionalProperties": false,
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "meter_reading"
            ],
            "description": "Operation discriminator. Required for meter reading operations."
          },
          "meter": {
            "type": "object",
            "required": [
              "unique_ids"
            ],
            "additionalProperties": false,
            "description": "Reference to the meter this reading belongs to.",
            "properties": {
              "unique_ids": {
                "type": "object",
                "minProperties": 1,
                "additionalProperties": {
                  "oneOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "number"
                    }
                  ]
                },
                "description": "Attribute values that uniquely identify the meter, as `{ attribute: value }`.",
                "example": {
                  "meter_number": "M-001"
                }
              }
            }
          },
          "counter": {
            "type": "object",
            "required": [
              "unique_ids"
            ],
            "additionalProperties": false,
            "description": "Optional reference to the meter counter (register) the reading belongs to. Required\nwhen `reading_matching` is `strict-date`.\n",
            "properties": {
              "unique_ids": {
                "type": "object",
                "minProperties": 1,
                "additionalProperties": {
                  "oneOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "number"
                    }
                  ]
                },
                "description": "Attribute values that uniquely identify the counter, as `{ attribute: value }`.",
                "example": {
                  "obis_number": "1-0:1.8.0"
                }
              }
            }
          },
          "mode": {
            "type": "string",
            "enum": [
              "upsert",
              "delete"
            ],
            "default": "upsert",
            "description": "Operation mode: `upsert` creates or updates the reading (default), `delete` removes it."
          },
          "reading_matching": {
            "type": "string",
            "enum": [
              "external_id",
              "strict-date"
            ],
            "description": "Strategy for matching this reading against existing readings. `external_id` (the\ndefault) matches by the reading's `external_id`; `strict-date` matches by meter +\ncounter + direction + date and REQUIRES `counter`. Useful when readings originate in\nthe portal and are echoed back by the ERP with truncated timestamps.\n"
          },
          "attributes": {
            "type": "object",
            "required": [
              "external_id",
              "timestamp",
              "source",
              "value"
            ],
            "description": "The reading data. Additional keys (e.g. `direction`, `reason`, `read_by`, `status`,\n`metadata`) pass through to the metering service verbatim.\n",
            "properties": {
              "external_id": {
                "oneOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "number"
                  }
                ],
                "description": "External identifier of the reading (numbers are coerced to strings)."
              },
              "timestamp": {
                "type": "string",
                "description": "When the reading was taken. ISO 8601 - either `YYYY-MM-DD` or\n`YYYY-MM-DDTHH:mm:ss` (optional fractional seconds and `Z` / `±HH:mm` offset).\nOther formats (e.g. `DD.MM.YYYY`, epoch numbers) are rejected.\n",
                "example": "2026-08-24T06:00:00Z"
              },
              "source": {
                "type": "string",
                "enum": [
                  "ECP",
                  "ERP",
                  "360",
                  "journey-submission"
                ],
                "description": "Origin system of the reading."
              },
              "value": {
                "oneOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "string"
                  }
                ],
                "description": "The reading value - a number, or a numeric string (coerced to a number)."
              }
            }
          }
        }
      },
      "DirectRelationValue": {
        "type": "object",
        "description": "An entity attribute value that establishes relations to other entities. Written on any\nattribute inside a direct entity operation's `attributes`.\n\nTwo forms are accepted: a bare array of items (shorthand for `_set`, i.e. replace), or\nan object with EXACTLY ONE of the operations `_set` (replace), `_append` (merge,\ndeduplicated by entity id), `_append_all` (merge without deduplication). No other\noperation keys are accepted.\n",
        "required": [
          "$relation"
        ],
        "additionalProperties": false,
        "properties": {
          "$relation": {
            "oneOf": [
              {
                "type": "array",
                "description": "Shorthand for `_set` - replaces the attribute's relations with these items.",
                "items": {
                  "$ref": "#/components/schemas/DirectRelationItem"
                }
              },
              {
                "type": "object",
                "description": "Exactly one of `_set`, `_append`, `_append_all` must be given.",
                "additionalProperties": false,
                "properties": {
                  "_set": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/DirectRelationItem"
                    }
                  },
                  "_append": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/DirectRelationItem"
                    }
                  },
                  "_append_all": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/DirectRelationItem"
                    }
                  }
                }
              }
            ]
          }
        }
      },
      "DirectRelationItem": {
        "type": "object",
        "description": "One relation target: either an already-resolved reference (`entity_id`), or a lookup by\nunique ids (`schema` + `unique_ids`, resolved by the pipeline like any other unique-id\nlookup, including stub creation and retry when the target does not exist yet). The two\nforms are mutually exclusive. Use `_id` as the sole `unique_ids` key for a direct-id\nlookup.\n",
        "additionalProperties": false,
        "properties": {
          "entity_id": {
            "type": "string",
            "description": "Epilot entity id of the relation target. Must not be combined with `schema` / `unique_ids`."
          },
          "schema": {
            "type": "string",
            "description": "Entity schema slug of the relation target. Required when `entity_id` is not given.",
            "example": "contact"
          },
          "unique_ids": {
            "type": "object",
            "minProperties": 1,
            "additionalProperties": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                }
              ]
            },
            "description": "Attribute values that uniquely identify the target. Required when `entity_id` is not given.",
            "example": {
              "customer_number": "CUST-12345"
            }
          },
          "unique_id_types": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "enum": [
                "email",
                "phone"
              ]
            },
            "description": "Optional per-field type hints for repeatable unique-id fields, as on the entity operation."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Optional relation tags (e.g. `primary`, `billing`)."
          }
        }
      },
      "DirectRelationRefValue": {
        "type": "object",
        "description": "An entity attribute value that references an ITEM INSIDE a repeatable attribute of\nanother entity - for example one address out of a contact's `address` list. The target\nentity is looked up by unique ids, then the item at `path` matching `value` is\nreferenced (creating it when missing).\n\nThe same operation envelope as `$relation` applies: a bare array is shorthand for\n`_set`, otherwise exactly one of `_set`, `_append`, `_append_all`.\n",
        "required": [
          "$relation_ref"
        ],
        "additionalProperties": false,
        "properties": {
          "$relation_ref": {
            "oneOf": [
              {
                "type": "array",
                "description": "Shorthand for `_set`.",
                "items": {
                  "$ref": "#/components/schemas/DirectRelationRefItem"
                }
              },
              {
                "type": "object",
                "description": "Exactly one of `_set`, `_append`, `_append_all` must be given.",
                "additionalProperties": false,
                "properties": {
                  "_set": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/DirectRelationRefItem"
                    }
                  },
                  "_append": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/DirectRelationRefItem"
                    }
                  },
                  "_append_all": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/DirectRelationRefItem"
                    }
                  }
                }
              }
            ]
          }
        }
      },
      "DirectRelationRefItem": {
        "type": "object",
        "description": "One relation-ref target - the entity to look up, and the repeatable item to reference on it.",
        "required": [
          "schema",
          "unique_ids",
          "path",
          "value"
        ],
        "additionalProperties": false,
        "properties": {
          "schema": {
            "type": "string",
            "minLength": 1,
            "description": "Entity schema slug of the target entity.",
            "example": "contact"
          },
          "unique_ids": {
            "type": "object",
            "minProperties": 1,
            "additionalProperties": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                }
              ]
            },
            "description": "Attribute values that uniquely identify the target entity."
          },
          "unique_id_types": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "enum": [
                "email",
                "phone"
              ]
            },
            "description": "Optional per-field type hints for repeatable unique-id fields."
          },
          "path": {
            "type": "string",
            "minLength": 1,
            "description": "The attribute on the TARGET entity that holds the repeatable array (e.g. `address`)."
          },
          "value": {
            "description": "The item to match (or create) at `path` - e.g. an address object.",
            "example": {
              "street": "Main Street",
              "city": "Berlin"
            }
          }
        }
      },
      "DirectSimulationRequest": {
        "type": "object",
        "description": "Request for a direct-mode dry run: the `direct: true` use case configuration to test\nagainst, and the payload to validate and translate.\n",
        "required": [
          "event_configuration",
          "payload"
        ],
        "properties": {
          "event_configuration": {
            "allOf": [
              {
                "$ref": "#/components/schemas/InboundIntegrationEventConfiguration"
              }
            ],
            "description": "The direct use case configuration to simulate against. `direct` must be `true`;\n`entities` acts as the optional allowlist exactly as at runtime. This is the same\nconfiguration format stored on the inbound use case resource, so a configuration can\nbe tested before saving it.\n"
          },
          "payload": {
            "oneOf": [
              {
                "type": "string",
                "description": "The direct payload as a JSON string (as it would be sent in the event's `payload`)."
              },
              {
                "$ref": "#/components/schemas/DirectPayload"
              }
            ],
            "description": "The direct payload to validate - a `DirectPayload` object, or its JSON string form."
          }
        }
      },
      "DirectSimulationResponse": {
        "type": "object",
        "description": "Result of a direct-mode dry run. `valid: false` responses list EVERY violation found\nacross all operations in `errors`. `valid: true` responses preview the internal updates\nthe pipeline would apply, plus non-blocking `warnings`.\n",
        "required": [
          "valid",
          "errors"
        ],
        "properties": {
          "valid": {
            "type": "boolean",
            "description": "Whether the payload would be accepted by the ingest endpoint."
          },
          "errors": {
            "type": "array",
            "description": "All validation errors found. Empty when `valid` is `true`.",
            "items": {
              "$ref": "#/components/schemas/DirectSimulationError"
            }
          },
          "warnings": {
            "type": "array",
            "description": "Non-blocking findings - e.g. unique-id fields not defined in the target entity\nschema (lookups would never match), or a failed server-side unique-id type\nderivation.\n",
            "items": {
              "$ref": "#/components/schemas/MappingSimulationWarning"
            }
          },
          "entity_updates": {
            "type": "array",
            "description": "The translated internal entity updates, exactly as the pipeline would process them.\nOnly present when `valid` is `true`.\n",
            "items": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/EntityUpdate"
                },
                {
                  "type": "object",
                  "properties": {
                    "unique_identifiers_metadata": {
                      "type": "object",
                      "description": "Per-field metadata for repeatable unique-id fields - explicit\n`unique_id_types` merged with server-side schema derivation. Absent when\nno field resolved to a repeatable type.\n",
                      "additionalProperties": {
                        "type": "object",
                        "properties": {
                          "fieldType": {
                            "type": "string",
                            "enum": [
                              "email",
                              "phone"
                            ]
                          },
                          "index": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              ]
            }
          },
          "meter_reading_updates": {
            "type": "array",
            "description": "The translated internal meter reading updates. Only present when `valid` is `true`.\n",
            "items": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/MeterReadingUpdate"
                },
                {
                  "type": "object",
                  "properties": {
                    "_config": {
                      "type": "object",
                      "description": "Per-reading pipeline configuration carried by the operation.",
                      "properties": {
                        "reading_matching": {
                          "type": "string",
                          "enum": [
                            "external_id",
                            "strict-date"
                          ]
                        }
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "DirectSimulationError": {
        "type": "object",
        "description": "One validation error found during a direct-mode dry run.",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "DIRECT_PAYLOAD_INVALID",
              "DIRECT_VERSION_UNSUPPORTED",
              "DIRECT_ENTITY_NOT_ALLOWED"
            ],
            "description": "Error class, matching the monitoring codes the ingest pipeline emits:\n`DIRECT_PAYLOAD_INVALID` (schema violation, JSON parse failure, or XML format),\n`DIRECT_VERSION_UNSUPPORTED` (unknown payload `version`),\n`DIRECT_ENTITY_NOT_ALLOWED` (entity or unique-id keys not permitted by the use\ncase's allowlist).\n"
          },
          "message": {
            "type": "string",
            "description": "Human-readable description, including the path of the offending element."
          },
          "operation_index": {
            "type": "integer",
            "description": "Zero-based index of the operation the error refers to, when attributable to one."
          }
        }
      },
      "EntityUpdate": {
        "type": "object",
        "required": [
          "entity_slug",
          "unique_identifiers",
          "attributes",
          "mode"
        ],
        "properties": {
          "entity_slug": {
            "type": "string",
            "description": "The entity type slug"
          },
          "unique_identifiers": {
            "type": "object",
            "description": "Unique identifier mappings for this entity",
            "additionalProperties": true
          },
          "attributes": {
            "type": "object",
            "description": "Mapped attribute values",
            "additionalProperties": true
          },
          "pricing": {
            "type": "object",
            "description": "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.",
            "required": [
              "config",
              "data"
            ],
            "properties": {
              "config": {
                "type": "object",
                "additionalProperties": true,
                "description": "The pricing configuration from the entity mapping"
              },
              "data": {
                "type": "array",
                "description": "The pricing input data extracted from the payload",
                "items": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "mode": {
            "type": "string",
            "enum": [
              "upsert",
              "delete",
              "purge"
            ],
            "description": "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."
          }
        }
      },
      "MeterReadingUpdate": {
        "type": "object",
        "required": [
          "meter",
          "attributes",
          "mode"
        ],
        "properties": {
          "meter": {
            "type": "object",
            "required": [
              "$entity_unique_ids"
            ],
            "properties": {
              "$entity_unique_ids": {
                "type": "object",
                "additionalProperties": true,
                "description": "Unique identifiers for the meter"
              }
            }
          },
          "meter_counter": {
            "type": "object",
            "properties": {
              "$entity_unique_ids": {
                "type": "object",
                "additionalProperties": true,
                "description": "Unique identifiers for the meter counter"
              }
            }
          },
          "attributes": {
            "type": "object",
            "additionalProperties": true,
            "description": "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": {
            "type": "string",
            "enum": [
              "upsert",
              "delete"
            ],
            "description": "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`."
          }
        }
      },
      "EntityPruneScopeUpdate": {
        "type": "object",
        "description": "A resolved entity prune scope. At runtime, entities of `entity_slug` found within `scope` whose unique identifiers are not listed in `keep_unique_ids` are removed using `deletion_mode`.",
        "required": [
          "entity_slug",
          "scope",
          "keep_unique_ids",
          "deletion_mode"
        ],
        "properties": {
          "entity_slug": {
            "type": "string",
            "description": "The entity type that would be pruned"
          },
          "scope": {
            "type": "object",
            "description": "The scope configuration resolved against the payload",
            "required": [
              "scope_mode"
            ],
            "properties": {
              "scope_mode": {
                "type": "string",
                "enum": [
                  "relations",
                  "query"
                ],
                "description": "How the entities in scope are found"
              },
              "schema": {
                "type": "string",
                "description": "Schema of the related entity defining the scope (`relations` scope_mode only)"
              },
              "unique_ids": {
                "type": "object",
                "additionalProperties": true,
                "description": "Resolved unique identifiers of the related scope entity (`relations` scope_mode only)"
              },
              "query": {
                "type": "object",
                "additionalProperties": true,
                "description": "Resolved query parameters used to find entities in scope (`query` scope_mode only)"
              }
            }
          },
          "keep_unique_ids": {
            "type": "array",
            "description": "Unique identifiers of the entities upserted by this payload. Everything else found in scope would be deleted.",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "deletion_mode": {
            "type": "string",
            "enum": [
              "delete",
              "purge"
            ],
            "description": "`delete` soft deletes the pruned entities, `purge` removes them permanently"
          }
        }
      },
      "MeterReadingPruneScopeUpdate": {
        "type": "object",
        "description": "A resolved meter reading prune scope. At runtime, readings of the given meter (+ counter) whose external ids are not listed in `keep_external_ids` are deleted.",
        "required": [
          "meter",
          "keep_external_ids"
        ],
        "properties": {
          "meter": {
            "type": "object",
            "required": [
              "$entity_unique_ids"
            ],
            "properties": {
              "$entity_unique_ids": {
                "type": "object",
                "additionalProperties": true,
                "description": "Unique identifiers for the meter"
              }
            }
          },
          "meter_counter": {
            "type": "object",
            "properties": {
              "$entity_unique_ids": {
                "type": "object",
                "additionalProperties": true,
                "description": "Unique identifiers for the meter counter"
              }
            }
          },
          "keep_external_ids": {
            "type": "array",
            "description": "External ids of the readings present in this payload. All other readings in scope would be deleted. An empty array means every reading in scope would be deleted.",
            "items": {
              "type": "string"
            }
          },
          "source": {
            "type": "string",
            "description": "When set, only readings with this source are eligible for pruning (e.g. `ERP`)"
          }
        }
      },
      "IntegrationConfigurationV1": {
        "type": "object",
        "required": [
          "mapping"
        ],
        "properties": {
          "version": {
            "type": "string",
            "enum": [
              "1.0"
            ],
            "default": "1.0",
            "description": "Mapping specification version"
          },
          "mapping": {
            "type": "object",
            "required": [
              "objects"
            ],
            "properties": {
              "objects": {
                "type": "object",
                "description": "[v1.0] Object type mappings",
                "additionalProperties": {
                  "$ref": "#/components/schemas/IntegrationObjectV1"
                }
              }
            }
          }
        }
      },
      "IntegrationObjectV1": {
        "type": "object",
        "required": [
          "unique_ids",
          "fields"
        ],
        "properties": {
          "unique_ids": {
            "type": "object",
            "description": "Mapping of entity types to their unique identifier field mappings",
            "additionalProperties": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "description": "List of local keys (must be present under the mapping \"fields\"!)"
                  }
                },
                {
                  "type": "object",
                  "description": "Map of remote keys to local entity keys",
                  "additionalProperties": {
                    "type": "string"
                  }
                }
              ]
            }
          },
          "fields": {
            "type": "array",
            "description": "Field mapping definitions",
            "items": {
              "$ref": "#/components/schemas/IntegrationFieldV1"
            }
          }
        }
      },
      "IntegrationFieldV1": {
        "type": "object",
        "required": [
          "entity",
          "attribute"
        ],
        "properties": {
          "entity": {
            "type": "string",
            "description": "Target entity slug"
          },
          "attribute": {
            "type": "string",
            "description": "Target attribute name"
          },
          "field": {
            "type": "string",
            "description": "Source field name (mutually exclusive with jsonataExpression)"
          },
          "jsonataExpression": {
            "type": "string",
            "description": "JSONata expression for transformation (mutually exclusive with field)"
          }
        }
      },
      "IntegrationConfigurationV2": {
        "type": "object",
        "required": [
          "version",
          "mapping"
        ],
        "properties": {
          "version": {
            "type": "string",
            "enum": [
              "2.0"
            ],
            "description": "Mapping specification version"
          },
          "mapping": {
            "type": "object",
            "required": [
              "events"
            ],
            "properties": {
              "events": {
                "type": "object",
                "description": "[v2.0] Event type mappings",
                "additionalProperties": {
                  "$ref": "#/components/schemas/InboundIntegrationEventConfiguration"
                }
              }
            }
          }
        }
      },
      "OutboundMapping": {
        "type": "object",
        "required": [
          "name",
          "enabled",
          "delivery"
        ],
        "description": "A mapping that delivers an event to an external system by one of three mechanisms — pushed to a webhook (with a JSONata payload transformation), made available on the pull-based poll queue (raw event payload, no transformation), or handed to a file_proxy use case that uploads files to an external document system (a pointer only; the referenced use case owns the payload and the fan-out)",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for this mapping; generated by the API when omitted"
          },
          "name": {
            "type": "string",
            "description": "Human-readable name for this mapping",
            "example": "ERP Contract Sync"
          },
          "jsonata_expression": {
            "type": "string",
            "description": "JSONata expression to transform the event payload. Required for webhook delivery, ignored for poll delivery, and rejected for file_proxy delivery — a file_proxy payload is built by the referenced use case's steps, so accepting an expression here would silently do nothing.",
            "example": "{ \"id\": entity._id, \"customer\": entity.customer_name }"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether this mapping is active",
            "default": true
          },
          "delivery": {
            "$ref": "#/components/schemas/DeliveryConfig"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the mapping was created"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the mapping was last updated"
          }
        }
      },
      "DeliveryConfig": {
        "description": "Configuration for how the event should be delivered. webhook = push delivery via svc-webhooks (JSONata-transformed payload); poll = pull-based queue delivery where the consumer fetches items via the poll API (raw event payload); file_proxy = one push per event attachment to an external document system, through a file_proxy use case (JSONata-transformed payload carrying the file bytes)",
        "oneOf": [
          {
            "$ref": "#/components/schemas/WebhookDeliveryConfig"
          },
          {
            "$ref": "#/components/schemas/PollDeliveryConfig"
          },
          {
            "$ref": "#/components/schemas/FileProxyDeliveryConfig"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "webhook": "#/components/schemas/WebhookDeliveryConfig",
            "poll": "#/components/schemas/PollDeliveryConfig",
            "file_proxy": "#/components/schemas/FileProxyDeliveryConfig"
          }
        }
      },
      "WebhookDeliveryConfig": {
        "type": "object",
        "required": [
          "type",
          "webhook_id"
        ],
        "description": "Push delivery of the transformed event to a webhook via svc-webhooks",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "webhook"
            ],
            "description": "Delivery mechanism type"
          },
          "webhook_id": {
            "type": "string",
            "description": "Reference to the webhook configuration in svc-webhooks"
          },
          "webhook_name": {
            "type": "string",
            "description": "Cached webhook name for display purposes"
          },
          "webhook_url": {
            "type": "string",
            "description": "Cached webhook URL for display purposes"
          }
        }
      },
      "PollDeliveryConfig": {
        "type": "object",
        "required": [
          "type"
        ],
        "description": "Pull-based queue delivery. Items carry the raw standardized event-catalog payload; no JSONata mapping is applied in poll mode. Consumers fetch and acknowledge items via the poll API.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "poll"
            ],
            "description": "Delivery mechanism type"
          },
          "retention_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 90,
            "default": 30,
            "description": "How long undelivered queue items are retained before expiry"
          },
          "poison_policy": {
            "type": "string",
            "enum": [
              "dead_letter",
              "block"
            ],
            "default": "dead_letter",
            "description": "What happens when an item exhausts max_delivery_attempts: dead_letter routes the exhausted item to the dead-letter queue and advances past it so the stream keeps flowing; block halts the queue at that item until operator/consumer action removes it."
          },
          "max_delivery_attempts": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 5,
            "description": "Maximum delivery attempts before the poison_policy is applied"
          }
        }
      },
      "FileProxyDeliveryConfig": {
        "type": "object",
        "required": [
          "type",
          "use_case_slug"
        ],
        "description": "Push delivery to an external document system through a `file_proxy` use case.\n\nA pure pointer, deliberately. The outbound use case decides WHEN to deliver — its event\nname plus `event_filter` — and the referenced `file_proxy` use case decides WHAT and HOW:\nwhich items to fan out over (`fan_out`), what values to build (each\nstep's `body_jsonata`), and the steps that send them. Keeping every file concern on the\nfile_proxy side is what lets one upload recipe be shared by several event subscriptions\nwithout duplicating any of it.\n\n`jsonata_expression` on the mapping is NOT used for this delivery type and is rejected.\n\nThe slug is resolved at delivery time, not at save time, so use cases can be saved in any\norder; a dangling reference surfaces as a `USE_CASE_NOT_FOUND` monitoring event naming\nthe referencing mapping.\n",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "file_proxy"
            ],
            "description": "Delivery mechanism type"
          },
          "use_case_slug": {
            "type": "string",
            "description": "Slug of a `file_proxy` use case with `direction: upload` in the SAME integration.\n",
            "example": "wemag_d3_document_upload"
          }
        }
      },
      "EntitySyncStatusResponse": {
        "type": "object",
        "required": [
          "entity_id",
          "sync_states"
        ],
        "properties": {
          "entity_id": {
            "type": "string",
            "format": "uuid",
            "description": "The entity the sync states belong to"
          },
          "sync_states": {
            "type": "array",
            "description": "One entry per integration that has processed an inbound event for the entity",
            "items": {
              "$ref": "#/components/schemas/EntitySyncState"
            }
          }
        }
      },
      "EntitySyncState": {
        "type": "object",
        "description": "Inbound sync state of one entity for one integration. Carries two\nindependent groups: the entity-level fields (`last_synced_at`, …) and,\non meter/meter_counter entities, the readings-level fields\n(`readings_last_synced_at`, …). Either group can be present alone — a\nmeter whose readings sync via the ERP but whose entity was never\nitself inbound-mapped carries only the readings group.\n",
        "required": [
          "entity_id",
          "integration_id",
          "entity_slug"
        ],
        "properties": {
          "entity_id": {
            "type": "string",
            "format": "uuid",
            "description": "The entity ID"
          },
          "integration_id": {
            "type": "string",
            "format": "uuid",
            "description": "The integration that synchronized the entity"
          },
          "entity_slug": {
            "type": "string",
            "description": "The entity schema slug",
            "example": "contract"
          },
          "use_case_id": {
            "type": "string",
            "description": "The inbound use case that last processed the entity"
          },
          "last_synced_at": {
            "type": "string",
            "format": "date-time",
            "description": "Last time an inbound ERP event for this entity was processed —\nincluding no-op checks that changed nothing on the entity.\n"
          },
          "last_changed_at": {
            "type": "string",
            "format": "date-time",
            "description": "Last time an inbound ERP event actually changed the entity (create/patch/delete)"
          },
          "last_operation": {
            "type": "string",
            "enum": [
              "create",
              "patch",
              "delete",
              "no-op"
            ],
            "description": "Outcome of the most recent inbound sync check"
          },
          "last_event_id": {
            "type": "string",
            "description": "The event ID of the most recent inbound sync check"
          },
          "readings_last_synced_at": {
            "type": "string",
            "format": "date-time",
            "description": "Last time meter readings for this meter/meter_counter entity were\nreceived and processed from the ERP. Only present on meter and\nmeter_counter entities. The metering path cannot detect no-ops,\nso there is no separate readings changed-at.\n"
          },
          "readings_last_operation": {
            "type": "string",
            "enum": [
              "upsert",
              "delete"
            ],
            "description": "Outcome of the most recent inbound readings sync"
          },
          "readings_last_event_id": {
            "type": "string",
            "description": "The event ID of the most recent inbound readings sync"
          }
        }
      },
      "OutboundStatusResponse": {
        "type": "object",
        "required": [
          "useCases"
        ],
        "properties": {
          "useCases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutboundUseCaseStatus"
            }
          }
        }
      },
      "OutboundUseCaseStatus": {
        "type": "object",
        "required": [
          "useCaseId",
          "name",
          "useCaseEnabled",
          "status"
        ],
        "properties": {
          "useCaseId": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the use case"
          },
          "name": {
            "type": "string",
            "description": "Human-readable name of the use case"
          },
          "useCaseEnabled": {
            "type": "boolean",
            "description": "Whether the use case is enabled"
          },
          "eventCatalogEvent": {
            "type": "string",
            "description": "The Event Catalog event name that triggers this outbound flow",
            "example": "contract.created"
          },
          "eventEnabled": {
            "type": "boolean",
            "nullable": true,
            "description": "Whether the event is enabled in Event Catalog. Null if the API is unreachable."
          },
          "webhooks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookStatus"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "conflict",
              "disabled"
            ],
            "description": "Overall status of the use case:\n- 'ok': Use case is enabled and all dependencies are properly configured\n- 'conflict': Use case has configuration issues (disabled events/webhooks while enabled)\n- 'disabled': Use case is disabled\n"
          },
          "conflicts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutboundConflict"
            },
            "description": "List of detected conflicts, if any"
          },
          "poll": {
            "$ref": "#/components/schemas/OutboundPollStatus"
          },
          "file_proxy": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutboundFileProxyTargetStatus"
            },
            "description": "One entry per file_proxy mapping on this use case. Present only on use cases with a\nfile_proxy delivery mapping.\n"
          }
        }
      },
      "OutboundFileProxyTargetStatus": {
        "type": "object",
        "description": "Resolution state of one file_proxy mapping's referenced upload use case.\n\nBecause the transport lives on a separate use case, edits to the endpoint or credentials\ndo not appear in THIS use case's history. `target_updated_at` is the hook that lets an\noperator notice a target changed underneath a delivery that started failing.\n",
        "required": [
          "mapping_id",
          "use_case_slug",
          "resolved"
        ],
        "properties": {
          "mapping_id": {
            "type": "string",
            "description": "The mapping holding this file_proxy delivery"
          },
          "use_case_slug": {
            "type": "string",
            "description": "Slug of the referenced file_proxy use case"
          },
          "resolved": {
            "type": "boolean",
            "description": "Whether the slug currently resolves to an enabled `file_proxy` use case with\n`direction: upload` in this integration. References are resolved at delivery time,\nso `false` is a live warning rather than a save-time error.\n"
          },
          "target_use_case_id": {
            "type": "string",
            "format": "uuid",
            "description": "Id of the referenced use case — absent when unresolved"
          },
          "target_enabled": {
            "type": "boolean",
            "description": "Whether the referenced use case is enabled — absent when unresolved"
          },
          "target_updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the referenced use case was last modified — absent when unresolved. Surfaced\nbecause a change here is invisible in this use case's own history diff.\n"
          },
          "unresolved_reason": {
            "type": "string",
            "enum": [
              "not_found",
              "wrong_type",
              "wrong_direction",
              "disabled"
            ],
            "description": "Why the reference did not resolve — absent when `resolved` is true"
          }
        }
      },
      "OutboundPollStatus": {
        "type": "object",
        "description": "Queue/consumer health for a poll-mode use case. Present only on use\ncases with a poll delivery mapping — webhook-only use cases are\nunaffected. Depth/age/DLQ numbers are first-page approximations.\n",
        "required": [
          "queue_depth",
          "oldest_unconsumed_age_seconds",
          "last_poll_at",
          "last_ack_at",
          "blocked",
          "dlq_count"
        ],
        "properties": {
          "queue_depth": {
            "type": "integer",
            "description": "Unconsumed messages attributable to this use case (first-page approximation)"
          },
          "oldest_unconsumed_age_seconds": {
            "type": "integer",
            "nullable": true,
            "description": "Age of the oldest unconsumed message in seconds — null when the queue is empty"
          },
          "last_poll_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Timestamp of the last successful poll lease — null before the first poll"
          },
          "last_ack_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Timestamp of the last committed acknowledgment — null before the first ack"
          },
          "blocked": {
            "type": "boolean",
            "description": "Whether the integration's outbound stream is currently halted by a\nblocked head message (poison_policy 'block'). Stream-level flag —\nthe same value is reported on every poll use case of the\nintegration, because the stream spans them.\n"
          },
          "dlq_count": {
            "type": "integer",
            "description": "Dead-lettered messages of this use case awaiting redrive or expiry (first-page approximation)"
          }
        }
      },
      "WebhookStatus": {
        "type": "object",
        "required": [
          "webhookId"
        ],
        "properties": {
          "webhookId": {
            "type": "string",
            "description": "Unique identifier for the webhook"
          },
          "webhookName": {
            "type": "string",
            "description": "Human-readable name of the webhook"
          },
          "enabled": {
            "type": "boolean",
            "nullable": true,
            "description": "Whether the webhook is enabled. Null if the API is unreachable."
          }
        }
      },
      "OutboundConflict": {
        "type": "object",
        "required": [
          "type",
          "message"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "event_disabled",
              "all_webhooks_disabled",
              "event_enabled_while_disabled",
              "webhook_enabled_while_disabled",
              "stream_blocked",
              "dlq_items_present"
            ],
            "description": "Type of conflict:\n- 'event_disabled': Event catalog event is disabled while use case is enabled\n- 'all_webhooks_disabled': All webhooks are disabled while use case is enabled\n- 'event_enabled_while_disabled': Event is enabled while use case is disabled\n- 'webhook_enabled_while_disabled': A webhook is enabled while use case is disabled\n- 'stream_blocked': The outbound stream is halted by a poison message awaiting operator action or consumer ack (poll mode)\n- 'dlq_items_present': Dead-lettered messages await redrive or expiry (poll mode)\n"
          },
          "webhookId": {
            "type": "string",
            "description": "Webhook ID (only present for webhook_enabled_while_disabled conflicts)"
          },
          "message": {
            "type": "string",
            "description": "Human-readable description of the conflict"
          }
        }
      },
      "PollOutboundMessagesRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "limit": {
            "type": "integer",
            "maximum": 100,
            "minimum": 1,
            "default": 10,
            "description": "Maximum number of messages to lease in this batch. The ~5.5 MB\nresponse cap may truncate the batch earlier when payloads are\nlarge — the leased run shrinks accordingly.\n"
          }
        }
      },
      "OutboundMessage": {
        "type": "object",
        "required": [
          "id",
          "lease_token",
          "use_case_id",
          "event_name",
          "event_id",
          "group",
          "payload",
          "enqueued_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque message id (msg_…) — stable per message across leases"
          },
          "lease_token": {
            "type": "string",
            "description": "Opaque lease token (lt_…) — echo back on ack"
          },
          "use_case_id": {
            "type": "string",
            "description": "The poll-mode use case that produced this message"
          },
          "event_name": {
            "type": "string",
            "description": "Standardized event name (e.g. contract.updated)"
          },
          "event_id": {
            "type": "string",
            "description": "Unique id of the originating event"
          },
          "group": {
            "type": "string",
            "description": "Ordering group — messages sharing a group are strictly ordered,\ndistinct groups are independent. Constant \"0\" in v1.\n"
          },
          "payload": {
            "type": "object",
            "additionalProperties": true,
            "description": "The raw standardized event-catalog event, always inlined as-is"
          },
          "enqueued_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the message was enqueued"
          }
        }
      },
      "PollOutboundMessagesResponse": {
        "type": "object",
        "required": [
          "messages",
          "visibility_timeout_seconds",
          "has_more"
        ],
        "properties": {
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutboundMessage"
            },
            "description": "The leased batch in strict stream order (empty when contended or drained)"
          },
          "visibility_timeout_seconds": {
            "type": "integer",
            "description": "Effective visibility timeout for this lease — a per-integration\nserver-side setting (default 300 seconds)\n"
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether more messages are available beyond this batch"
          }
        }
      },
      "AckOutboundMessagesRequest": {
        "type": "object",
        "required": [
          "acks"
        ],
        "properties": {
          "acks": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "id",
                "lease_token"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "description": "The message id (msg_…) to acknowledge"
                },
                "lease_token": {
                  "type": "string",
                  "description": "The lease token (lt_…) returned by the poll that leased this message"
                }
              }
            }
          }
        }
      },
      "AckResult": {
        "type": "object",
        "required": [
          "id",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The acknowledged message id"
          },
          "status": {
            "type": "string",
            "enum": [
              "accepted",
              "rejected"
            ]
          },
          "reason": {
            "type": "string",
            "enum": [
              "stale_lease",
              "out_of_order",
              "not_found"
            ],
            "description": "Rejection reason — present only when status is rejected"
          }
        }
      },
      "AckOutboundMessagesResponse": {
        "type": "object",
        "required": [
          "results"
        ],
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AckResult"
            },
            "description": "Per-id acknowledgement outcome"
          }
        }
      },
      "OutboundDlqMessage": {
        "type": "object",
        "required": [
          "id",
          "use_case_id",
          "event_name",
          "event_id",
          "dead_lettered_at",
          "delivery_attempts"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque message id (msg_…) of the dead-lettered message"
          },
          "use_case_id": {
            "type": "string",
            "description": "The poll-mode use case that produced this message"
          },
          "event_name": {
            "type": "string",
            "description": "Standardized event name (e.g. contract.updated)"
          },
          "event_id": {
            "type": "string",
            "description": "Unique id of the originating event"
          },
          "enqueued_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the message was originally enqueued"
          },
          "dead_lettered_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the message was dead-lettered"
          },
          "delivery_attempts": {
            "type": "integer",
            "description": "Delivery attempts at the moment the message was dead-lettered"
          },
          "reason": {
            "type": "string",
            "description": "Why the message was dead-lettered (policy or operator reason)"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the DLQ entry expires (retention window re-armed at dead-letter time)"
          }
        }
      },
      "OutboundDlqListResponse": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutboundDlqMessage"
            },
            "description": "Dead-lettered messages, oldest first"
          },
          "next_token": {
            "type": "string",
            "description": "Opaque pagination token — present when more entries exist"
          }
        }
      },
      "RedriveOutboundDlqRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "ids"
        ],
        "properties": {
          "ids": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "type": "string"
            },
            "description": "Message ids (msg_…) of the DLQ entries to redrive"
          }
        }
      },
      "RedriveOutboundDlqResult": {
        "type": "object",
        "required": [
          "id",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The requested message id"
          },
          "status": {
            "type": "string",
            "enum": [
              "redriven",
              "not_found"
            ],
            "description": "Outcome — `redriven` re-enqueued at the tail; `not_found` for\nunknown ids or entries concurrently redriven/expired\n"
          }
        }
      },
      "RedriveOutboundDlqResponse": {
        "type": "object",
        "required": [
          "results"
        ],
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RedriveOutboundDlqResult"
            },
            "description": "Per-id redrive outcome, input order preserved"
          }
        }
      },
      "UnblockOutboundStreamRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "reason": {
            "type": "string",
            "maxLength": 500,
            "description": "Operator reason recorded on the dead-lettered head"
          }
        }
      },
      "UnblockOutboundStreamResponse": {
        "type": "object",
        "required": [
          "unblocked"
        ],
        "properties": {
          "unblocked": {
            "type": "boolean",
            "description": "Whether a blocked head was skipped (false = no-op, stream was not blocked)"
          },
          "dead_lettered_id": {
            "type": "string",
            "description": "Message id of the skipped head — present only when unblocked is true"
          }
        }
      },
      "RelationConfig": {
        "type": "object",
        "required": [
          "operation"
        ],
        "properties": {
          "operation": {
            "type": "string",
            "enum": [
              "_set",
              "_append",
              "_append_all"
            ],
            "description": "Relation operation:\n- '_set': Replace all existing relations with the specified items\n- '_append': Add new unique items to existing relations (deduplicates by entity_id)\n- '_append_all': Add all items to existing relations (no deduplication, allows duplicates)\n"
          },
          "items": {
            "type": "array",
            "description": "Array of relation item configurations",
            "items": {
              "$ref": "#/components/schemas/RelationItemConfig"
            }
          },
          "jsonataExpression": {
            "type": "string",
            "description": "JSONata expression that returns relation items array (alternative to 'items')"
          }
        }
      },
      "RelationItemConfig": {
        "type": "object",
        "required": [
          "entity_schema",
          "unique_ids"
        ],
        "properties": {
          "entity_schema": {
            "type": "string",
            "description": "Related entity schema"
          },
          "_tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Optional tags for this relation"
          },
          "unique_ids": {
            "type": "array",
            "description": "Unique identifier mappings for the related entity",
            "items": {
              "$ref": "#/components/schemas/RelationUniqueIdField"
            }
          }
        }
      },
      "RelationUniqueIdField": {
        "type": "object",
        "required": [
          "attribute"
        ],
        "properties": {
          "attribute": {
            "type": "string",
            "description": "Target attribute name in the related entity"
          },
          "_type": {
            "$ref": "#/components/schemas/RepeatableFieldType"
          },
          "field": {
            "type": "string",
            "description": "Source field name from the event data"
          },
          "jsonataExpression": {
            "type": "string",
            "description": "JSONata expression to compute the value"
          },
          "constant": {
            "description": "Constant value (any type)"
          }
        }
      },
      "RelationRefsConfig": {
        "type": "object",
        "description": "Configuration for relation references ($relation_ref).\nRelation references link to a specific item within a repeatable attribute on a related entity.\nCommon use case: referencing a specific address within a contact's address list.\n",
        "required": [
          "operation"
        ],
        "properties": {
          "operation": {
            "type": "string",
            "enum": [
              "_set",
              "_append",
              "_append_all"
            ],
            "description": "Relation reference operation:\n- '_set': Replace all existing relation_refs with the specified items\n- '_append': Add new unique items to existing relation_refs (deduplicates by entity_id + _id)\n- '_append_all': Add all items to existing relation_refs (no deduplication, allows duplicates)\n"
          },
          "items": {
            "type": "array",
            "description": "Array of relation reference item configurations",
            "items": {
              "$ref": "#/components/schemas/RelationRefItemConfig"
            }
          },
          "jsonataExpression": {
            "type": "string",
            "description": "JSONata expression that returns relation_ref items array (alternative to 'items')"
          }
        }
      },
      "RelationRefItemConfig": {
        "type": "object",
        "description": "Configuration for a single relation reference item",
        "required": [
          "entity_schema",
          "unique_ids",
          "path",
          "value"
        ],
        "properties": {
          "entity_schema": {
            "type": "string",
            "description": "Schema of the related entity (e.g., \"contact\")"
          },
          "unique_ids": {
            "type": "array",
            "description": "Unique identifier mappings for the related entity",
            "items": {
              "$ref": "#/components/schemas/RelationUniqueIdField"
            }
          },
          "path": {
            "type": "string",
            "description": "Attribute path on the related entity (e.g., \"address\")"
          },
          "value": {
            "$ref": "#/components/schemas/RelationRefValueConfig"
          }
        }
      },
      "RelationRefValueConfig": {
        "type": "object",
        "description": "Configuration for the value to set on the related entity's attribute",
        "required": [
          "attribute"
        ],
        "properties": {
          "attribute": {
            "type": "string",
            "description": "Target attribute name on the related entity"
          },
          "operation": {
            "type": "string",
            "enum": [
              "_set",
              "_append",
              "_append_all"
            ],
            "description": "Operation for the attribute value:\n- '_set': Replace the attribute value\n- '_append': Add new unique items (deduplicates)\n- '_append_all': Add all items (no deduplication)\n"
          },
          "field": {
            "type": "string",
            "description": "Source field name from the event data"
          },
          "jsonataExpression": {
            "type": "string",
            "description": "JSONata expression to compute the value"
          },
          "constant": {
            "description": "Constant value (any type)"
          }
        }
      },
      "RepeatableFieldType": {
        "type": "string",
        "enum": [
          "email",
          "phone"
        ],
        "description": "Type hint for repeatable fields that require special search handling.\nThese fields are stored as arrays of objects (e.g., email: [{ email: \"value\" }]).\n"
      },
      "ReplayEventsRequest": {
        "type": "object",
        "required": [
          "event_ids"
        ],
        "properties": {
          "event_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "maxItems": 100,
            "description": "List of event IDs to replay. Maximum 100 events per request."
          }
        }
      },
      "QueryEventsRequest": {
        "type": "object",
        "properties": {
          "event_id": {
            "type": "string",
            "description": "Filter by event ID"
          },
          "event_type": {
            "type": "string",
            "enum": [
              "CREATE",
              "UPDATE",
              "DELETE"
            ],
            "description": "Filter by event type"
          },
          "correlation_id": {
            "type": "string",
            "description": "Filter by correlation ID"
          },
          "object_type": {
            "type": "string",
            "description": "Filter by object type"
          },
          "event_name": {
            "type": "string",
            "description": "Filter by event name (alias for object_type)"
          },
          "use_case_id": {
            "type": "string",
            "minLength": 1,
            "description": "Filter by use case ID"
          },
          "limit": {
            "type": "integer",
            "description": "Maximum number of results to return",
            "example": 25,
            "default": 25,
            "minimum": 1,
            "maximum": 25
          },
          "cursor": {
            "type": "object",
            "description": "Cursor for pagination. Use the next_cursor from the previous response to get the next page.",
            "properties": {
              "event_time": {
                "type": "string",
                "format": "date-time",
                "description": "Timestamp from the last event in the previous page",
                "example": "2025-10-31T12:34:56Z"
              },
              "event_id": {
                "type": "string",
                "description": "Event ID from the last event in the previous page",
                "example": "evt_1234567890abcdef"
              }
            }
          }
        }
      },
      "QueryInboundMonitoringEventsRequest": {
        "type": "object",
        "properties": {
          "use_case_id": {
            "type": "string",
            "format": "uuid",
            "description": "Filter by use case ID"
          },
          "event_type": {
            "type": "string",
            "enum": [
              "CREATE",
              "UPDATE",
              "DELETE",
              "TRIGGER"
            ],
            "description": "Filter by event type"
          },
          "sync_type": {
            "type": "string",
            "enum": [
              "entity",
              "meter_reading",
              "webhook",
              "api_deprecation"
            ],
            "description": "Filter by sync type"
          },
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error",
              "skipped",
              "warning"
            ],
            "description": "Filter by processing status"
          },
          "error_category": {
            "type": "string",
            "enum": [
              "validation",
              "configuration",
              "downstream_api",
              "timeout",
              "system"
            ],
            "description": "Filter by error category (only applicable when status=error)"
          },
          "correlation_id": {
            "type": "string",
            "description": "Filter by correlation ID"
          },
          "object_type": {
            "type": "string",
            "description": "Filter by object type (e.g., 'contract', 'meter')"
          },
          "event_name": {
            "type": "string",
            "description": "Filter by event name (alias for object_type)"
          },
          "event_id": {
            "type": "string",
            "description": "Filter by event ID to find a specific event"
          },
          "from_date": {
            "type": "string",
            "format": "date-time",
            "description": "Filter events from this date (inclusive)",
            "example": "2025-01-01T00:00:00Z"
          },
          "to_date": {
            "type": "string",
            "format": "date-time",
            "description": "Filter events until this date (inclusive)",
            "example": "2025-01-31T23:59:59Z"
          },
          "limit": {
            "type": "integer",
            "description": "Maximum number of results to return",
            "example": 50,
            "default": 50,
            "minimum": 1,
            "maximum": 100
          },
          "cursor": {
            "type": "object",
            "description": "Cursor for pagination",
            "properties": {
              "completed_at": {
                "type": "string",
                "format": "date-time",
                "description": "Timestamp from the last event in the previous page"
              },
              "event_id": {
                "type": "string",
                "description": "Event ID from the last event in the previous page"
              }
            }
          }
        }
      },
      "QueryAccessLogsRequest": {
        "type": "object",
        "properties": {
          "token_id": {
            "type": "string",
            "description": "Filter by a specific access token ID (e.g., 'api_5ZugdRXasLfWBypHi93Fk').\nMust be one of the access_token_ids linked to the integration.\nIf omitted, returns logs for all access tokens linked to the integration.\n",
            "example": "api_5ZugdRXasLfWBypHi93Fk"
          },
          "service": {
            "type": "string",
            "description": "Filter by service name (e.g., 'entity', 'metering', 'submission-api')",
            "example": "entity"
          },
          "method": {
            "type": "string",
            "enum": [
              "GET",
              "POST",
              "PUT",
              "PATCH",
              "DELETE",
              "OPTIONS",
              "HEAD"
            ],
            "description": "Filter by HTTP method"
          },
          "path": {
            "type": "string",
            "description": "Filter by request path (partial match)",
            "example": "/v1/entity"
          },
          "status": {
            "type": "integer",
            "description": "Filter by HTTP status code",
            "example": 200
          },
          "from_date": {
            "type": "string",
            "format": "date-time",
            "description": "Filter logs from this date (inclusive)",
            "example": "2025-01-01T00:00:00Z"
          },
          "to_date": {
            "type": "string",
            "format": "date-time",
            "description": "Filter logs until this date (inclusive)",
            "example": "2025-01-31T23:59:59Z"
          },
          "limit": {
            "type": "integer",
            "description": "Maximum number of results to return",
            "example": 50,
            "default": 50,
            "minimum": 1,
            "maximum": 100
          },
          "cursor": {
            "type": "object",
            "description": "Cursor for pagination (infinite scroll)",
            "properties": {
              "timestamp": {
                "type": "string",
                "format": "date-time",
                "description": "Timestamp from the last log entry in the previous page"
              },
              "request_id": {
                "type": "string",
                "description": "Request ID from the last log entry in the previous page"
              }
            }
          }
        }
      },
      "AccessLogEntry": {
        "type": "object",
        "properties": {
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "When the request was made"
          },
          "environment": {
            "type": "string",
            "description": "Environment (e.g., 'dev', 'prod')"
          },
          "service": {
            "type": "string",
            "description": "Service name (e.g., 'entity', 'metering')"
          },
          "request_id": {
            "type": "string",
            "description": "Unique request identifier"
          },
          "method": {
            "type": "string",
            "description": "HTTP method"
          },
          "path": {
            "type": "string",
            "description": "Request path"
          },
          "status": {
            "type": "integer",
            "description": "HTTP status code"
          },
          "response_latency_ms": {
            "type": "integer",
            "description": "Response latency in milliseconds"
          },
          "response_length": {
            "type": "integer",
            "description": "Response body length in bytes"
          },
          "token_id": {
            "type": "string",
            "description": "Access token identifier"
          },
          "org_id": {
            "type": "string",
            "description": "Organization ID"
          },
          "origin": {
            "type": "string",
            "description": "Request origin header"
          },
          "source_ip": {
            "type": "string",
            "description": "Client IP address"
          }
        }
      },
      "GetMonitoringStatsRequest": {
        "type": "object",
        "properties": {
          "from_date": {
            "type": "string",
            "format": "date-time",
            "description": "Start date for statistics period (inclusive)",
            "example": "2025-01-01T00:00:00Z"
          },
          "to_date": {
            "type": "string",
            "format": "date-time",
            "description": "End date for statistics period (inclusive)",
            "example": "2025-01-31T23:59:59Z"
          },
          "inbound_group_by": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "use_case_id",
                "sync_type",
                "status",
                "error_category",
                "object_type",
                "event_name",
                "date"
              ]
            },
            "description": "Fields to group inbound statistics by",
            "example": [
              "use_case_id",
              "status"
            ]
          },
          "outbound_group_by": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "event_name",
                "status",
                "webhook_config_id",
                "date"
              ]
            },
            "description": "Fields to group outbound statistics by",
            "example": [
              "event_name",
              "status"
            ]
          }
        }
      },
      "GetMonitoringTimeSeriesRequest": {
        "type": "object",
        "required": [
          "from_date",
          "interval"
        ],
        "properties": {
          "from_date": {
            "type": "string",
            "format": "date-time",
            "description": "Start date for the time series (inclusive)",
            "example": "2025-01-01T00:00:00Z"
          },
          "to_date": {
            "type": "string",
            "format": "date-time",
            "description": "End date for the time series (inclusive). Defaults to current time if not specified.",
            "example": "2025-01-31T23:59:59Z"
          },
          "interval": {
            "type": "string",
            "enum": [
              "5m",
              "10m",
              "30m",
              "1h",
              "3h",
              "1d"
            ],
            "description": "The time bucket interval for aggregation",
            "example": "1h"
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound",
              "both"
            ],
            "default": "both",
            "description": "Filter by event direction. Defaults to both.",
            "example": "both"
          }
        }
      },
      "TimeSeriesBucket": {
        "type": "object",
        "required": [
          "timestamp"
        ],
        "properties": {
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "The start timestamp of the bucket"
          },
          "inbound": {
            "type": "object",
            "nullable": true,
            "description": "Inbound event counts for this bucket. Null when direction is outbound.",
            "properties": {
              "success_count": {
                "type": "integer"
              },
              "error_count": {
                "type": "integer"
              },
              "warning_count": {
                "type": "integer"
              },
              "skipped_count": {
                "type": "integer"
              },
              "total_count": {
                "type": "integer"
              }
            }
          },
          "outbound": {
            "type": "object",
            "nullable": true,
            "description": "Outbound event counts for this bucket. Null when direction is inbound.",
            "properties": {
              "success_count": {
                "type": "integer"
              },
              "error_count": {
                "type": "integer"
              },
              "pending_count": {
                "type": "integer"
              },
              "total_count": {
                "type": "integer"
              }
            }
          }
        }
      },
      "QueryOutboundMonitoringEventsRequest": {
        "type": "object",
        "properties": {
          "event_name": {
            "type": "string",
            "description": "Filter by event name (event_catalog_event). If not specified, returns events for all linked event names in the integration's outbound use cases.",
            "example": "automation_flow_target"
          },
          "status": {
            "type": "string",
            "enum": [
              "succeeded",
              "failed",
              "pending"
            ],
            "description": "Filter by delivery status"
          },
          "webhook_config_id": {
            "type": "string",
            "description": "Filter by webhook configuration ID"
          },
          "from_date": {
            "type": "string",
            "format": "date-time",
            "description": "Filter events from this date (inclusive)",
            "example": "2025-01-01T00:00:00Z"
          },
          "to_date": {
            "type": "string",
            "format": "date-time",
            "description": "Filter events until this date (inclusive)",
            "example": "2025-01-31T23:59:59Z"
          },
          "limit": {
            "type": "integer",
            "description": "Maximum number of results to return",
            "example": 50,
            "default": 50,
            "minimum": 1,
            "maximum": 100
          },
          "cursor": {
            "type": "object",
            "description": "Cursor for pagination",
            "properties": {
              "created_at": {
                "type": "string",
                "format": "date-time",
                "description": "Timestamp from the last event in the previous page"
              },
              "event_id": {
                "type": "string",
                "description": "Event ID from the last event in the previous page"
              }
            }
          }
        }
      },
      "OutboundMonitoringEvent": {
        "type": "object",
        "required": [
          "org_id",
          "event_id",
          "event_name",
          "status",
          "created_at"
        ],
        "properties": {
          "org_id": {
            "type": "string",
            "description": "Organization ID"
          },
          "event_id": {
            "type": "string",
            "description": "Unique event identifier"
          },
          "event_name": {
            "type": "string",
            "description": "Event name (event_catalog_event)"
          },
          "status": {
            "type": "string",
            "enum": [
              "succeeded",
              "failed",
              "pending"
            ],
            "description": "Delivery status"
          },
          "url": {
            "type": "string",
            "description": "Target URL"
          },
          "http_method": {
            "type": "string",
            "description": "HTTP method used (e.g., POST)"
          },
          "http_response": {
            "type": "object",
            "description": "HTTP response details (status_code, message, headers)",
            "additionalProperties": true
          },
          "webhook_config_id": {
            "type": "string",
            "description": "Webhook configuration ID"
          },
          "metadata": {
            "type": "object",
            "description": "Additional metadata (webhook_id, organization details, correlation_id, etc.)",
            "additionalProperties": true
          },
          "execution_context": {
            "type": "object",
            "description": "Execution context (execution_arn, state_machine_arn, etc.)",
            "additionalProperties": true
          },
          "payload": {
            "type": "object",
            "description": "Payload that was sent",
            "additionalProperties": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the event was created"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the event was last updated"
          }
        }
      },
      "MonitoringStats": {
        "type": "object",
        "required": [
          "inbound",
          "outbound"
        ],
        "properties": {
          "inbound": {
            "type": "object",
            "description": "Statistics for inbound (ERP sync) events",
            "required": [
              "total_events",
              "success_count",
              "error_count",
              "skipped_count"
            ],
            "properties": {
              "total_events": {
                "type": "integer",
                "description": "Total number of inbound events in the period"
              },
              "total_correlations": {
                "type": "integer",
                "description": "Total number of unique correlation IDs"
              },
              "success_count": {
                "type": "integer",
                "description": "Number of successful events"
              },
              "error_count": {
                "type": "integer",
                "description": "Number of failed events"
              },
              "skipped_count": {
                "type": "integer",
                "description": "Number of skipped events"
              },
              "warning_count": {
                "type": "integer",
                "description": "Number of warning events"
              },
              "success_rate": {
                "type": "number",
                "format": "float",
                "description": "Success rate as percentage (0-100)"
              },
              "last_error_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Timestamp of the most recent error"
              },
              "breakdown": {
                "type": "array",
                "description": "Statistics breakdown by requested inbound_group_by fields",
                "items": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "outbound": {
            "type": "object",
            "description": "Statistics for outbound (webhook delivery) events",
            "required": [
              "total_events",
              "success_count",
              "error_count"
            ],
            "properties": {
              "total_events": {
                "type": "integer",
                "description": "Total number of outbound events in the period"
              },
              "success_count": {
                "type": "integer",
                "description": "Number of successful deliveries"
              },
              "error_count": {
                "type": "integer",
                "description": "Number of failed deliveries"
              },
              "pending_count": {
                "type": "integer",
                "description": "Number of pending deliveries"
              },
              "success_rate": {
                "type": "number",
                "format": "float",
                "description": "Success rate as percentage (0-100)"
              },
              "last_error_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Timestamp of the most recent error"
              },
              "breakdown": {
                "type": "array",
                "description": "Statistics breakdown by requested outbound_group_by fields",
                "items": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      },
      "InboundMonitoringEvent": {
        "type": "object",
        "required": [
          "org_id",
          "event_id",
          "object_type",
          "sync_type",
          "status",
          "received_at",
          "completed_at"
        ],
        "properties": {
          "org_id": {
            "type": "string",
            "description": "Organization ID"
          },
          "event_id": {
            "type": "string",
            "description": "Unique event identifier"
          },
          "correlation_id": {
            "type": "string",
            "nullable": true,
            "description": "Correlation ID for tracing related events"
          },
          "integration_id": {
            "type": "string",
            "nullable": true,
            "description": "Integration ID"
          },
          "use_case_id": {
            "type": "string",
            "nullable": true,
            "description": "Use case ID"
          },
          "event_type": {
            "type": "string",
            "enum": [
              "CREATE",
              "UPDATE",
              "DELETE",
              "TRIGGER"
            ],
            "description": "Type of event (optional for V3 events)"
          },
          "object_type": {
            "type": "string",
            "description": "Type of object being synced (e.g., 'contract', 'meter')"
          },
          "sync_type": {
            "type": "string",
            "enum": [
              "entity",
              "meter_reading",
              "webhook",
              "api_deprecation"
            ],
            "description": "Type of sync operation"
          },
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error",
              "skipped",
              "warning"
            ],
            "description": "Processing status"
          },
          "error_code": {
            "type": "string",
            "nullable": true,
            "description": "Error code (when status=error)"
          },
          "error_message": {
            "type": "string",
            "nullable": true,
            "description": "Error message (when status=error)"
          },
          "error_category": {
            "type": "string",
            "nullable": true,
            "enum": [
              "validation",
              "configuration",
              "downstream_api",
              "timeout",
              "system"
            ],
            "description": "Error category (when status=error)"
          },
          "processing_duration_ms": {
            "type": "integer",
            "nullable": true,
            "description": "Processing duration in milliseconds"
          },
          "received_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the event was received"
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "description": "When processing completed"
          }
        }
      },
      "QueryMonitoringEventsV2Request": {
        "type": "object",
        "properties": {
          "use_case_id": {
            "type": "string",
            "description": "Filter by use case ID (UUID). Empty string matches \"General\" events. Special value \"__unknown__\" matches events whose use_case_id does not belong to any currently configured use case for this integration (excludes \"General\" events).\n"
          },
          "use_case_type": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound",
              "file_proxy",
              "managed_call",
              "secure_proxy"
            ],
            "description": "Filter by use case type (replaces direction)"
          },
          "level": {
            "type": "string",
            "enum": [
              "success",
              "error",
              "info",
              "warning",
              "skipped"
            ],
            "description": "Filter by event level. `skipped` is accepted but can never match: the v2 pipeline normalises it to `info`, and the table's enum has no such member. It is retained here — and ONLY here, not on `MonitoringEventV2` — so that a client with a saved `level=skipped` filter keeps getting an empty 200 rather than a 400 from request validation. Use `info` instead."
          },
          "code": {
            "type": "string",
            "description": "Filter by taxonomy code (e.g. OAUTH2_TOKEN_FAILURE, HTTP_502). Accepts any `MonitoringCode` value, or an `HTTP_{status}` family value."
          },
          "event_id": {
            "type": "string",
            "description": "Filter by trigger/inbound event ID"
          },
          "correlation_id": {
            "type": "string",
            "description": "Filter by correlation ID for tracing"
          },
          "from_date": {
            "type": "string",
            "format": "date-time",
            "description": "Filter events from this date (inclusive)",
            "example": "2025-01-01T00:00:00Z"
          },
          "to_date": {
            "type": "string",
            "format": "date-time",
            "description": "Filter events until this date (inclusive)",
            "example": "2025-01-31T23:59:59Z"
          },
          "limit": {
            "type": "integer",
            "description": "Maximum number of results to return",
            "example": 50,
            "default": 50,
            "minimum": 1,
            "maximum": 100
          },
          "cursor": {
            "type": "object",
            "description": "Cursor for pagination (from previous response's next_cursor)",
            "properties": {
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "id": {
                "type": "string",
                "format": "uuid"
              }
            }
          }
        }
      },
      "ExternalMonitoringSpan": {
        "type": "object",
        "properties": {
          "correlation_id": {
            "type": "string",
            "description": "Trace id — links this span to the epilot trace. Unique per business operation. Required."
          },
          "level": {
            "type": "string",
            "description": "Span outcome level — one of success | error | warning | info. Drives the server-assigned EXTERNAL_* code, coloring, and alerting. Required.\n"
          },
          "use_case_slug": {
            "type": "string",
            "description": "Business use case slug (e.g. \"business_partner\"). Resolved server-side against the integration's configured use cases to a use_case_id/use_case_type; stored as the grouping dimension. Portable — no epilot-internal UUIDs required.\n"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time",
            "description": "External clock time the span occurred; stored as created_at."
          },
          "message": {
            "type": "string",
            "description": "Human-readable line shown in the trace and event tables."
          },
          "detail": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true,
            "description": "Free-form context (step, http_status, reason, …). The place for external-system specificity — the taxonomy code stays clean (EXTERNAL_*).\n"
          }
        },
        "description": "A single monitoring span produced by an external system. `correlation_id`, `level`, `use_case_slug`, `occurred_at` and `message` are all required and `level` must be one of success|error|warning|info — but these are validated PER SPAN at ingest and reported in the per-item `results[]`, so one malformed span never fails the whole batch (the fields are intentionally not marked `required`/`enum` at the schema level to preserve that partial-acceptance behavior). The client does NOT send a `code` (server-assigned from `level`), a `source` (origin is the EXTERNAL_* code prefix), or span/event ids (the external system owns its own identity and dedup).\n"
      },
      "IngestExternalMonitoringEventsRequest": {
        "type": "object",
        "required": [
          "events"
        ],
        "properties": {
          "events": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/ExternalMonitoringSpan"
            },
            "description": "Batch of external monitoring spans (max 100 per request)."
          }
        }
      },
      "IngestExternalMonitoringEventsResponse": {
        "type": "object",
        "required": [
          "accepted",
          "rejected"
        ],
        "properties": {
          "accepted": {
            "type": "integer",
            "description": "Number of spans accepted and republished onto the monitoring bus."
          },
          "rejected": {
            "type": "integer",
            "description": "Number of spans rejected in validation. See `results` for reasons."
          },
          "results": {
            "type": "array",
            "description": "Per-span results; present for rejections in a mixed batch.",
            "items": {
              "type": "object",
              "required": [
                "index",
                "status"
              ],
              "properties": {
                "index": {
                  "type": "integer",
                  "description": "Zero-based index of the span in the request `events` array."
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "accepted",
                    "rejected"
                  ]
                },
                "reason": {
                  "type": "string",
                  "description": "Rejection reason (present when status is rejected)."
                }
              }
            }
          }
        }
      },
      "MonitoringTraceResponse": {
        "type": "object",
        "required": [
          "correlation_id",
          "status",
          "span_count",
          "truncated",
          "spans"
        ],
        "properties": {
          "correlation_id": {
            "type": "string",
            "description": "The trace id these spans share."
          },
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error",
              "warning",
              "info"
            ],
            "description": "Rolled-up status across all spans (error > warning > success > info)."
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "created_at of the earliest span (null when the trace is empty)."
          },
          "ended_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "created_at of the latest returned span (null when the trace is empty)."
          },
          "span_count": {
            "type": "integer",
            "description": "Number of spans RETURNED (== spans.length; capped)."
          },
          "truncated": {
            "type": "boolean",
            "description": "True when the trace has more spans than the cap; the returned window is the earliest spans, so ended_at/status reflect only that window.\n"
          },
          "spans": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MonitoringEventV2"
            },
            "description": "All spans sharing the correlation_id, chronological. External-origin spans carry an EXTERNAL_* code; epilot spans carry an epilot code or empty (success).\n"
          },
          "inbound_event": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true,
            "description": "The epilot inbound event \"head\" (raw payload), when present."
          }
        }
      },
      "MonitoringCode": {
        "type": "string",
        "description": "The monitoring code taxonomy — every code the Integration Toolkit itself emits,\nwith a fixed level (see the published code reference at\nhttps://docs.epilot.io/docs/integrations/integration-toolkit/monitoring/codes).\n\nThis schema exists so consumers can import the union as a type. It is\ndeliberately NOT applied to `MonitoringEventV2.code` or to the query filter:\nalongside these, the secure proxy emits an unbounded `HTTP_{status}` family for\nan upstream refusal, so constraining those fields to this enum would reject a\nlegitimate value — `code` stays a plain string there on purpose.\n\nKept in lock-step with the `MonitoringErrorCode` taxonomy in erp-utils by\n`monitoring-code-enum.test.ts`, which fails if the two diverge.\n",
        "enum": [
          "ACK_CONFIRMED",
          "ACK_PENDING",
          "ACK_TIMEOUT",
          "ATTACHMENT_NOT_FOUND",
          "ATTRIBUTE_TYPE_MISMATCH",
          "DEPRECATED_ENDPOINT",
          "DIRECT_ENTITY_NOT_ALLOWED",
          "DIRECT_PAYLOAD_INVALID",
          "DIRECT_VERSION_UNSUPPORTED",
          "DUPLICATE_EVENT",
          "ENTITY_CREATED",
          "ENTITY_DELETED",
          "ENTITY_NO_OP",
          "ENTITY_REFERENCE_NOT_FOUND",
          "ENTITY_UPDATED",
          "EVENT_NOT_CONFIGURED",
          "EXTERNAL_API_ERROR",
          "EXTERNAL_ERROR",
          "EXTERNAL_INFO",
          "EXTERNAL_SUCCESS",
          "EXTERNAL_WARNING",
          "FAN_OUT_EMPTY",
          "FAN_OUT_INVALID_RESULT",
          "FILE_EXTRACTION_FAILED",
          "FILE_FETCH_FAILED",
          "FILE_PROXY_OK",
          "FILE_PROXY_UPLOADED",
          "FILE_PROXY_UPLOAD_ENQUEUED",
          "FILE_PROXY_UPLOAD_FAILED",
          "FILE_PROXY_UPLOAD_RETRYING",
          "FILE_TOO_LARGE",
          "INTEGRATION_NOT_FOUND",
          "INVALID_METER_READING_ATTRIBUTES",
          "LOOKUP_UNMAPPED",
          "MALFORMED_PAYLOAD",
          "MAPPING_EXPRESSION_FAILED",
          "METERING_API_ERROR",
          "METER_READING_DELETED",
          "METER_READING_GROUP_FAILED",
          "METER_READING_GROUP_RETRYING",
          "METER_READING_UPSERTED",
          "MISSING_REQUIRED_PARAM",
          "MISSING_UNIQUE_IDENTIFIERS",
          "MSG_ACKED",
          "MSG_DEAD_LETTERED",
          "MSG_ENQUEUED",
          "MSG_EXPIRED_UNPOLLED",
          "MSG_HEAD_BLOCKED",
          "OAUTH2_TOKEN_FAILURE",
          "PAYLOAD_TOO_LARGE",
          "PRUNE_SCOPE_COMPLETED",
          "PRUNE_SCOPE_PARTIAL_FAILURE",
          "RECURSION_DEPTH_EXCEEDED",
          "RELATION_REF_ITEM_NOT_FOUND",
          "RELATION_REF_VALUE_UNDEFINED",
          "REQUIRED_PARAM_MISSING",
          "SECURE_PROXY_DISABLED",
          "SECURE_PROXY_DOMAIN_BLOCKED",
          "SECURE_PROXY_DOMAIN_NOT_ALLOWED",
          "SECURE_PROXY_ERROR",
          "SECURE_PROXY_INVALID_CONFIG",
          "SECURE_PROXY_INVALID_TYPE",
          "SECURE_PROXY_INVALID_URL",
          "SECURE_PROXY_IP_BLOCKED",
          "SECURE_PROXY_IP_NOT_ALLOWED",
          "SECURE_PROXY_NOT_FOUND",
          "SECURE_PROXY_UNAVAILABLE",
          "SIGNATURE_VERIFICATION_FAILED",
          "SIGNATURE_VERIFICATION_UNAVAILABLE",
          "SOFT_DELETED_ENTITY_MATCHED",
          "STEP_DISABLED",
          "TIMEOUT",
          "UNIQUE_ID_MULTIPLE_MATCHES",
          "UNIQUE_ID_NOT_IN_SCHEMA",
          "UNKNOWN_ERROR",
          "USE_CASE_DISABLED",
          "USE_CASE_INVALID_TYPE",
          "USE_CASE_MISSING_CONFIG",
          "USE_CASE_NOT_FOUND",
          "WEBHOOK_DELIVERED"
        ]
      },
      "MonitoringEventV2": {
        "type": "object",
        "required": [
          "id",
          "org_id",
          "integration_id",
          "event_id",
          "use_case_type",
          "level",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique monitoring event ID"
          },
          "org_id": {
            "type": "string",
            "description": "Organization ID"
          },
          "integration_id": {
            "type": "string",
            "description": "Integration ID"
          },
          "event_id": {
            "type": "string",
            "description": "Trigger/inbound event ID (groups related monitoring events)"
          },
          "correlation_id": {
            "type": "string",
            "description": "Correlation ID for tracing. Empty when unavailable."
          },
          "use_case_id": {
            "type": "string",
            "description": "Use case ID. Empty for system-level events (\"General\")."
          },
          "use_case_type": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound",
              "file_proxy",
              "managed_call",
              "secure_proxy",
              ""
            ],
            "description": "Use case type. Empty for system-level events."
          },
          "level": {
            "type": "string",
            "enum": [
              "success",
              "error",
              "info",
              "warning"
            ],
            "description": "Event outcome level"
          },
          "code": {
            "type": "string",
            "description": "Taxonomy code (e.g. OAUTH2_TOKEN_FAILURE, HTTP_502). Empty for success. One of `MonitoringCode`, or an `HTTP_{status}` family value from the secure proxy — which is why this is a string rather than that enum."
          },
          "message": {
            "type": "string",
            "description": "Human-readable message. Empty when not applicable."
          },
          "detail": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true,
            "description": "Event-specific JSON data. Structure determined by `code`."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the monitoring event was created"
          }
        }
      },
      "GetMonitoringStatsV2Request": {
        "type": "object",
        "properties": {
          "from_date": {
            "type": "string",
            "format": "date-time",
            "description": "Start of the time range",
            "example": "2025-01-01T00:00:00Z"
          },
          "to_date": {
            "type": "string",
            "format": "date-time",
            "description": "End of the time range",
            "example": "2025-01-31T23:59:59Z"
          },
          "use_case_type": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound",
              "file_proxy",
              "managed_call",
              "secure_proxy"
            ],
            "description": "Filter stats by a single use case type"
          },
          "use_case_types": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "inbound",
                "outbound",
                "file_proxy",
                "managed_call",
                "secure_proxy"
              ]
            },
            "description": "Filter stats to this set of use case types (matches any). Takes precedence over `use_case_type` when both are present. Used by the notification producers to scope alerts/digests to the integration's `monitoredUseCases`."
          },
          "group_by": {
            "type": "string",
            "enum": [
              "use_case_id",
              "use_case_type",
              "level",
              "code",
              "date"
            ],
            "description": "Field to group the breakdown by"
          },
          "source": {
            "type": "string",
            "enum": [
              "monitoring",
              "incoming"
            ],
            "default": "monitoring",
            "description": "Data source for the stats. \"monitoring\" (default) aggregates processed events from erp_monitoring_v2 — this counts every event produced throughout the processing tree (fan-out children, post-actions, relation resolutions, etc.). \"incoming\" counts only the initial inbound events actually received (distinct event_id from erp_incoming_events); only group_by=use_case_id is supported and status/level breakdown is not available for this source (success/error/warning/skipped counts are returned as 0).\n"
          }
        }
      },
      "MonitoringStatsV2": {
        "type": "object",
        "required": [
          "total_events",
          "success_count",
          "error_count",
          "warning_count",
          "skipped_count",
          "info_count"
        ],
        "properties": {
          "total_events": {
            "type": "integer",
            "description": "Total number of events in the period"
          },
          "success_count": {
            "type": "integer",
            "description": "Number of successful events"
          },
          "error_count": {
            "type": "integer",
            "description": "Number of error events"
          },
          "warning_count": {
            "type": "integer",
            "description": "Number of warning events"
          },
          "skipped_count": {
            "type": "integer",
            "deprecated": true,
            "description": "Always 0. The v2 pipeline has no `skipped` level; v1 `skipped` is normalised to `info`. Retained for backward compatibility — use `info_count`."
          },
          "info_count": {
            "type": "integer",
            "description": "Number of info-level events (ACK lifecycle, poll-queue MSG_*, duplicate events, fan-out anchors). Info events are counted in `total_events` but are excluded from `success_rate`."
          },
          "ack_timeout_count": {
            "type": "integer",
            "description": "Number of ACK_TIMEOUT events (acknowledgement timed out)"
          },
          "success_rate": {
            "type": "number",
            "format": "float",
            "description": "Success rate as percentage (0-100)"
          },
          "last_error_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Timestamp of the most recent error"
          },
          "breakdown": {
            "type": "array",
            "description": "Statistics breakdown by requested group_by field",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "GetMonitoringTimeSeriesV2Request": {
        "type": "object",
        "required": [
          "from_date",
          "interval"
        ],
        "properties": {
          "from_date": {
            "type": "string",
            "format": "date-time",
            "description": "Start of the time range (required)",
            "example": "2025-01-01T00:00:00Z"
          },
          "to_date": {
            "type": "string",
            "format": "date-time",
            "description": "End of the time range (defaults to now)",
            "example": "2025-01-31T23:59:59Z"
          },
          "interval": {
            "type": "string",
            "enum": [
              "5m",
              "10m",
              "30m",
              "1h",
              "3h",
              "1d"
            ],
            "description": "Time bucket interval"
          },
          "use_case_type": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound",
              "file_proxy",
              "managed_call",
              "secure_proxy"
            ],
            "description": "Filter by use case type"
          },
          "group_by": {
            "type": "string",
            "enum": [
              "use_case_type",
              "use_case"
            ],
            "description": "Field to group the bucket breakdown by"
          }
        }
      },
      "TimeSeriesBreakdownItemV2": {
        "type": "object",
        "required": [
          "success_count",
          "error_count",
          "warning_count",
          "skipped_count",
          "info_count",
          "total_count"
        ],
        "properties": {
          "use_case_type": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound",
              "file_proxy",
              "managed_call",
              "secure_proxy"
            ],
            "description": "Present when grouping by use_case_type"
          },
          "use_case_id": {
            "type": "string",
            "description": "Present when grouping by use_case. Unknown historical IDs are collapsed into \"__unknown__\""
          },
          "success_count": {
            "type": "integer",
            "description": "Number of successful events in the breakdown item"
          },
          "error_count": {
            "type": "integer",
            "description": "Number of error events in the breakdown item"
          },
          "warning_count": {
            "type": "integer",
            "description": "Number of warning events in the breakdown item"
          },
          "skipped_count": {
            "type": "integer",
            "deprecated": true,
            "description": "Always 0. The v2 pipeline has no `skipped` level; v1 `skipped` is normalised to `info`. Retained for backward compatibility — use `info_count`."
          },
          "info_count": {
            "type": "integer",
            "description": "Number of info-level events in the breakdown item"
          },
          "total_count": {
            "type": "integer",
            "description": "Total events in the breakdown item"
          }
        }
      },
      "TimeSeriesBucketV2": {
        "type": "object",
        "required": [
          "timestamp",
          "total_count"
        ],
        "properties": {
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "Bucket start timestamp"
          },
          "success_count": {
            "type": "integer",
            "description": "Number of successful events in the bucket"
          },
          "error_count": {
            "type": "integer",
            "description": "Number of error events in the bucket"
          },
          "warning_count": {
            "type": "integer",
            "description": "Number of warning events in the bucket"
          },
          "skipped_count": {
            "type": "integer",
            "deprecated": true,
            "description": "Always 0. The v2 pipeline has no `skipped` level; v1 `skipped` is normalised to `info`. Retained for backward compatibility — use `info_count`."
          },
          "info_count": {
            "type": "integer",
            "description": "Number of info-level events in the bucket"
          },
          "total_count": {
            "type": "integer",
            "description": "Total events in the bucket"
          },
          "breakdown": {
            "type": "array",
            "description": "Statistics breakdown by the requested group_by field for this bucket",
            "items": {
              "$ref": "#/components/schemas/TimeSeriesBreakdownItemV2"
            }
          }
        }
      },
      "DocumentationPageSummary": {
        "type": "object",
        "description": "Documentation page metadata without the markdown content",
        "required": [
          "id",
          "integration_id",
          "scope",
          "title",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "'general' for the integration-wide page, otherwise the use case ID",
            "example": "general"
          },
          "integration_id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "scope": {
            "type": "string",
            "enum": [
              "integration",
              "use_case"
            ],
            "description": "Whether the page documents the whole integration or a single use case"
          },
          "use_case_id": {
            "type": "string",
            "format": "uuid",
            "description": "The linked use case. Only set when scope is use_case."
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 150
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "created_by": {
            "type": "string",
            "nullable": true,
            "description": "User ID that created the page",
            "readOnly": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated_by": {
            "type": "string",
            "nullable": true,
            "description": "User ID of the last edit",
            "readOnly": true
          }
        }
      },
      "DocumentationPage": {
        "description": "A markdown documentation page of an integration",
        "allOf": [
          {
            "$ref": "#/components/schemas/DocumentationPageSummary"
          },
          {
            "type": "object",
            "required": [
              "content"
            ],
            "properties": {
              "content": {
                "type": "string",
                "maxLength": 100000,
                "description": "Markdown source of the page"
              }
            }
          }
        ]
      },
      "UpsertDocumentationPageRequest": {
        "type": "object",
        "required": [
          "title",
          "content"
        ],
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 150
          },
          "content": {
            "type": "string",
            "maxLength": 100000,
            "description": "Markdown source of the page"
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Bad request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponseBase"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponseBase"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Unauthorized request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponseBase"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Forbidden - insufficient permissions",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponseBase"
            }
          }
        }
      },
      "Conflict": {
        "description": "Conflict - the resource is not in a state that allows this operation",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponseBase"
            }
          }
        }
      },
      "InternalServerError": {
        "description": "Internal Server Error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponseBase"
            }
          }
        }
      },
      "QueryInboundMonitoringEventsResponse": {
        "description": "Inbound monitoring events queried successfully",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "data": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InboundMonitoringEvent"
                  },
                  "description": "List of inbound monitoring events"
                },
                "next_cursor": {
                  "type": "object",
                  "nullable": true,
                  "description": "Cursor to fetch the next page. Null if no more results.",
                  "properties": {
                    "completed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "event_id": {
                      "type": "string"
                    }
                  }
                },
                "has_more": {
                  "type": "boolean",
                  "description": "Indicates if more results are available"
                }
              }
            }
          }
        }
      },
      "GetMonitoringStatsResponse": {
        "description": "Monitoring statistics retrieved successfully",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/MonitoringStats"
            }
          }
        }
      },
      "GetMonitoringTimeSeriesResponse": {
        "description": "Time-series aggregated event counts retrieved successfully",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "interval",
                "from_date",
                "to_date",
                "buckets"
              ],
              "properties": {
                "interval": {
                  "type": "string",
                  "enum": [
                    "5m",
                    "10m",
                    "30m",
                    "1h",
                    "3h",
                    "1d"
                  ],
                  "description": "The time bucket interval used for aggregation"
                },
                "from_date": {
                  "type": "string",
                  "format": "date-time",
                  "description": "Start date of the time series"
                },
                "to_date": {
                  "type": "string",
                  "format": "date-time",
                  "description": "End date of the time series"
                },
                "buckets": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TimeSeriesBucket"
                  },
                  "description": "List of time-series buckets with event counts"
                }
              }
            }
          }
        }
      },
      "QueryAccessLogsResponse": {
        "description": "Access logs queried successfully",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "data": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AccessLogEntry"
                  },
                  "description": "List of access log entries"
                },
                "next_cursor": {
                  "type": "object",
                  "nullable": true,
                  "description": "Cursor to fetch the next page. Null if no more results.",
                  "properties": {
                    "timestamp": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "request_id": {
                      "type": "string"
                    }
                  }
                },
                "has_more": {
                  "type": "boolean",
                  "description": "Indicates if more results are available"
                }
              }
            }
          }
        }
      },
      "QueryOutboundMonitoringEventsResponse": {
        "description": "Outbound monitoring events queried successfully",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "data": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/OutboundMonitoringEvent"
                  },
                  "description": "List of outbound monitoring events"
                },
                "next_cursor": {
                  "type": "object",
                  "nullable": true,
                  "description": "Cursor to fetch the next page. Null if no more results.",
                  "properties": {
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "event_id": {
                      "type": "string"
                    }
                  }
                },
                "has_more": {
                  "type": "boolean",
                  "description": "Indicates if more results are available"
                }
              }
            }
          }
        }
      },
      "ReplayEventsResponse": {
        "description": "Events replay initiated. Always 200 — per-event outcomes are reported in `results`, so inspect it (or compare `replayed` against the number of requested ids) rather than treating the status code as success.\n",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "replayed",
                "results"
              ],
              "properties": {
                "replayed": {
                  "type": "integer",
                  "description": "Number of events actually queued for re-processing.",
                  "example": 2
                },
                "results": {
                  "type": "array",
                  "description": "One entry per requested event id, in request order.",
                  "items": {
                    "type": "object",
                    "required": [
                      "event_id",
                      "status"
                    ],
                    "properties": {
                      "event_id": {
                        "type": "string",
                        "description": "The requested (source) event ID."
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "success",
                          "queued",
                          "skipped",
                          "ignored",
                          "not_found",
                          "error"
                        ],
                        "description": "Outcome for this event. `success`/`queued` means it was enqueued for re-processing. `not_found` means no inbound event with that ID exists for the organization. `skipped` means it was deduplicated, `ignored` means no enabled use case matched it, and `error` means it could not be queued (see `message`).\n"
                      },
                      "replay_event_id": {
                        "type": "string",
                        "description": "The new event ID assigned to the replayed event. Use it to follow the replay in monitoring.\n"
                      },
                      "message": {
                        "type": "string",
                        "description": "Human-readable detail for this outcome."
                      }
                    }
                  }
                },
                "event_ids": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "List of event IDs for which replay was requested"
                }
              }
            }
          }
        }
      },
      "ERPUpdatesResponse": {
        "description": "Some events failed to process",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "results": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "event_id",
                      "status"
                    ],
                    "properties": {
                      "event_id": {
                        "type": "string",
                        "description": "ID of the processed event"
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "success",
                          "error",
                          "skipped",
                          "ignored"
                        ],
                        "description": "Processing status for the event (skipped indicates duplicate deduplication_id, ignored indicates unconfigured event)"
                      },
                      "message": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "TriggerWebhookResponse": {
        "description": "Result of triggering an ERP synchronization event",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/TriggerWebhookResp"
            }
          }
        }
      },
      "QueryEventsResponse": {
        "description": "Events queried successfully",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "data": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ErpEvent"
                  },
                  "description": "List of erp events"
                },
                "next_cursor": {
                  "type": "object",
                  "nullable": true,
                  "description": "Cursor to fetch the next page. Null if no more results.",
                  "properties": {
                    "event_time": {
                      "type": "string",
                      "format": "date-time",
                      "example": "2025-10-31T12:34:56Z"
                    },
                    "event_id": {
                      "type": "string",
                      "example": "evt_1234567890abcdef"
                    }
                  }
                },
                "has_more": {
                  "type": "boolean",
                  "description": "Indicates if more results are available",
                  "example": true
                }
              }
            }
          }
        }
      },
      "QueryMonitoringEventsV2Response": {
        "description": "Monitoring events queried successfully",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "data": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MonitoringEventV2"
                  },
                  "description": "List of monitoring events"
                },
                "next_cursor": {
                  "type": "object",
                  "nullable": true,
                  "description": "Cursor to fetch the next page. Null if no more results.",
                  "properties": {
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                },
                "has_more": {
                  "type": "boolean",
                  "description": "Indicates if more results are available"
                }
              }
            }
          }
        }
      },
      "GetMonitoringStatsV2Response": {
        "description": "Monitoring statistics retrieved successfully",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/MonitoringStatsV2"
            }
          }
        }
      },
      "GetMonitoringTimeSeriesV2Response": {
        "description": "Time-series data retrieved successfully",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "interval": {
                  "type": "string",
                  "enum": [
                    "5m",
                    "10m",
                    "30m",
                    "1h",
                    "3h",
                    "1d"
                  ],
                  "description": "The bucket interval"
                },
                "from_date": {
                  "type": "string",
                  "format": "date-time"
                },
                "to_date": {
                  "type": "string",
                  "format": "date-time"
                },
                "buckets": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TimeSeriesBucketV2"
                  }
                }
              }
            }
          }
        }
      },
      "GetAssociatedMonitoringEventsResponse": {
        "description": "Associated monitoring events for the given event_id",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "monitoring_events": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MonitoringEventV2"
                  },
                  "description": "All monitoring events sharing this event_id, ordered by created_at ASC"
                },
                "inbound_event": {
                  "type": "object",
                  "nullable": true,
                  "additionalProperties": true,
                  "description": "The original inbound event payload from erp_incoming_events, if available"
                }
              }
            }
          }
        }
      }
    }
  }
}
