openapi: 3.0.3
info:
  title: ModernEDI Integration API
  version: 1.14.0
  license:
    name: Proprietary
    url: https://modernedi.com/terms
  description: |
    Customer-facing integration endpoints for ModernEDI tenants. Use this API when your ERP, WMS, integration service, or internal automation needs to receive inbound partner documents as mapped business payloads, send XML/JSON/text source inputs that ModernEDI turns into outbound X12, send already-generated X12 without applying a map, validate X12, or inspect transaction activity across the whole exchange.

    ModernEDI keeps X12 at the trading-partner boundary instead of making it the shape your internal systems have to speak. Incoming maps translate partner X12 into JSON, XML, HTML, text, or another business format your system can process. Outgoing maps do the reverse: your system sends an XML, JSON, or text source document plus optional runtime params, and ModernEDI selects the matching outgoing map, generates the X12 transaction body, syntax-checks it, and sends it over AS2.

    In the mapper editor, generated-X12 trace highlights can connect output values back to the map and source fixtures. XSLT maps trace generated values back to stylesheet instructions, source input values, and test params; JSLT maps use the same trace contract against ModernEDI's wrapper root, where source values live under `.input` and params under `.params`, including simple aliases such as `let po = .input`.

    A typical purchase-order-to-invoice round trip is:

    1. A partner sends an inbound 850 purchase order to ModernEDI.
    2. ModernEDI validates the inbound X12, applies the incoming map, and makes the mapped payload available through `GET /v1/mapped-outputs` or a mapped-output webhook.
    3. Call `POST /v1/mapped-outputs/{id}/ack` after your integration has durably stored the mapped output or enqueued it for reliable processing. This acknowledgment confirms custody, not later ERP or business processing.
    4. Your system builds an invoice, ASN, or other business reply as XML, JSON, or text.
    5. Call `POST /v1/as2/reply`; ModernEDI applies the matching outgoing map, generates X12, validates it, and sends the reply AS2 message.
    6. Use `/v1/integration/transactions` to confirm the inbound document, mapped output, generated outbound X12, AS2 receipts, 997s, and reply linkage.

    If you prefer push delivery, configure a per-partner mapped-output webhook in the ModernEDI workspace. ModernEDI sends the same mapped-output message shape to your HTTPS endpoint and automatically acknowledges the queue item only when your endpoint returns a 2xx response. Failed webhook attempts leave the message available through the pull API.

    Push webhook requests include `X-ModernEDI-Event`, `X-ModernEDI-Delivery-Id`, `X-ModernEDI-Timestamp`, and `X-ModernEDI-Signature`. The signature is `sha256=<hex hmac>` over `<timestamp>.<raw request body>` using the partner webhook signing secret.

    `/v1/as2/send` is available when your system is first in the chain, such as sending a purchase order or another outbound document that is not replying to a received document. `/v1/as2/carbonCopy` uses the same outgoing-map selection as the original partner, then sends the generated X12 envelope to a second partner.

    Trading partner records can be created before an AS2 profile is known, which lets teams publish and test maps during migration. Use `GET /v1/partners` to list the partner ids your integration client can send as `partnerId` for exact outgoing-map selection. These AS2 endpoints require the matched destination partner to have an AS2 connection configured at send time; otherwise ModernEDI rejects the request before delivery.

    Key fields in the mapped-output and reply loop:

    - `id`: mapped output id used in the `/v1/mapped-outputs/{id}/ack` path.
    - `receiptHandle`: token from the latest poll, required when marking mapped output as received.
    - `messageId`: original inbound AS2 message id; use it as `originalMessageId` for `/v1/as2/reply`.
    - `transactionKey`: transaction identifier within the AS2 message, usually `GS06#ST02`.
    - `mappedOutputKey`: unique mapped-output key inside the transaction.
    - `purpose`: why the map result exists. `PROCESSING` is a normal mapped output for your application workflow, while `ACKNOWLEDGMENT` is an automatically generated response document such as a purchase order acknowledgment.
    - `payload`: mapped document body; parse it according to `contentType`.
    - `extraFields`: delivered metadata selected by the incoming runtime map. Incoming maps start with ModernEDI's default metadata set; custom keys are merged with those defaults, and setting a default key to `false` intentionally omits it.
    - `delivery`: stored transaction handoff state. `acknowledged` means acknowledged by your integration; it does not prove how another application handled the document afterward.
    - `replyToMessageId`: present on outbound replies and points back to the inbound message.

    For a transaction detail view, start with the inbound transaction detail and show the original X12 document, mapped output records, MDN and 997 details, outbound replies from `relatedTransactions`, and the reply transaction's own MDN and 997 details.

    Common failures to handle include missing or invalid API keys, unknown partner names, missing outgoing maps, generated X12 syntax errors, `originalMessageId` not found, partner AS2 delivery failure, and mapped output returned again because it was not marked received before the visibility timeout.

    Every `/v1` response includes an `X-Request-Id` header. You may send a safe caller-generated `X-Request-Id` (1 to 128 characters using letters, numbers, `.`, `_`, `:`, `@`, `/`, `+`, `=`, or `-`); ModernEDI preserves it, or generates a replacement when it is absent or unsafe. Log the response value for both successful and failed calls so ModernEDI support can correlate the request with runtime logs. Error bodies repeat the same value as `error.requestId`.

    Integration API keys carry explicit least-privilege scopes. Read transaction history, mapped outputs, and usage with `transactions:read`; replay inbound transactions with `transactions:replay`; send or validate messages and acknowledge mapped outputs with `messages:write`; read partners, mappings, AS2 connections, the public AS2 profile, and webhook settings with `configuration:read`; and change those configuration objects with `configuration:write`. Every operation below states its required scope. A valid key without that scope receives `403 insufficient_scope` with the required and granted scopes in `error.details`.

    Structured JSON request bodies use strict schemas. Configuration writes and transaction replay reject unknown fields at any documented object level rather than ignoring them, and scalar values must use the documented JSON type; for example, `"true"` is not accepted for a boolean and numeric ids are not accepted as strings. `null` is accepted only where the schema explicitly allows it. These failures return `400 invalid_request` with the offending JSON field path in the message.

    Public transaction timestamps are UTC instants formatted with exactly nine fractional digits and `Z`, for example `2026-07-15T18:30:00.000000000Z`. Transaction date-time filters must include an explicit RFC 3339 offset; date-only filters represent UTC calendar days. Offset-less date-times are rejected.

    All customer-facing Integration API endpoints use `https://api.modernedi.com`. ModernEDI forwards outbound `/v1/as2/send`, `/v1/as2/reply`, `/v1/as2/carbonCopy`, and their `/v1/as2/x12/...` counterparts into the tenant AS2 runtime after authenticating the same Integration API key used by mapped-output and transaction-viewer endpoints.
servers:
  - url: https://api.modernedi.com
tags:
  - name: Partners
    description: Read and manage tenant trading-partner records and stable partner ids.
  - name: AS2 Connections
    description: Manage partner AS2 identifiers, destination URLs, public certificates, and transport settings.
  - name: Mappings
    description: Manage safe incoming and outgoing mapping configuration as source-controlled text.
  - name: Outbound AS2
    description: Send reply documents and standalone outbound AS2 messages through an existing ModernEDI partner connection.
  - name: Mapped Output Queue
    description: Receive mapped inbound documents by polling, or configure per-partner HTTPS push delivery in the workspace.
  - name: Transaction Viewer
    description: Read transaction history, documents, events, acknowledgements, linked replies, and safe response-only replays.
  - name: X12 Tools
    description: Validate X12 and discover the X12 versions and transaction sets available to the tenant.
  - name: Account
    description: Read current plan usage and enforcement status.
paths:
  /v1/partners:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    get:
      operationId: listIntegrationPartners
      tags:
        - Partners
      summary: List configured trading partners
      x-required-scope: configuration:read
      description: >
        **Required key scope:** `configuration:read`.

        Returns the tenant's active configured trading partners in workspace order. Retired partners are excluded.
        Use `partnerId` from this response as the `partnerId` query parameter
        on `/v1/as2/send`, `/v1/as2/reply`, or `/v1/as2/carbonCopy` when your
        integration needs exact partner selection instead of partner-name
        matching. The returned `name` is the configured partner name accepted
        as `customer` or `copyToCustomer`; the same id can be sent as
        `copyToPartnerId` for the carbon-copy recipient. Partners can appear before their AS2
        profile is complete so teams can publish and test maps during
        onboarding; `as2ConnectionConfigured` tells you whether live AS2
        delivery can use that partner yet.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl "https://api.modernedi.com/v1/partners" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY"
        - lang: Python
          label: Python
          source: |
            import os
            import requests

            response = requests.get(
                "https://api.modernedi.com/v1/partners",
                headers={"x-api-key": os.environ["MODERNEDI_INTEGRATION_API_KEY"]},
                timeout=30,
            )
            response.raise_for_status()
            partners = response.json()["partners"]
            partner_id_by_name = {partner["name"]: partner["partnerId"] for partner in partners}
        - lang: JavaScript
          label: JavaScript
          source: |
            const response = await fetch("https://api.modernedi.com/v1/partners", {
              headers: { "x-api-key": process.env.MODERNEDI_INTEGRATION_API_KEY },
            })

            if (!response.ok) {
              throw new Error(`ModernEDI partner list failed: ${response.status}`)
            }

            const { partners } = await response.json()
            const partnerIdByName = Object.fromEntries(
              partners.map((partner) => [partner.name, partner.partnerId]),
            )
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      responses:
        '200':
          description: Trading partners were returned.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartnerListResponse'
              example:
                success: true
                partners:
                  - partnerId: 1
                    name: Customer One
                    as2ConnectionId: 1
                    as2ConnectionConfigured: true
                    productionReady: true
                    testReady: true
                    carbonCopyOnly: false
                    ediSupportEmail: edi@example.com
                    unmappedInboundPolicy: MAPPED_ONLY
                    signingCertificateSlot: active
                    supportsNewlineAfterSegmentTerminator: false
                    x12:
                      production:
                        isaQualifier: ZZ
                        isaId: CUSTOMERONE
                        gsId: CUSTOMERONE
                      test:
                        isaQualifier: ZZ
                        isaId: CUSTOMERONET
                        gsId: CUSTOMERONET
                    etag: '"cca9acc426eb9eae4e9d34160c07ce49a50e0dedd0142908d8be0e8703e28a50"'
                  - partnerId: 2
                    name: Broker One
                    as2ConnectionId: 2
                    as2ConnectionConfigured: true
                    productionReady: true
                    testReady: true
                    carbonCopyOnly: true
                    ediSupportEmail: edi@example.com
                    unmappedInboundPolicy: MAPPED_ONLY
                    signingCertificateSlot: active
                    supportsNewlineAfterSegmentTerminator: false
                    x12:
                      production:
                        isaQualifier: ZZ
                        isaId: BROKERONE
                        gsId: BROKERONE
                      test:
                        isaQualifier: ZZ
                        isaId: BROKERONET
                        gsId: BROKERONET
                    etag: '"336e62f6260da31d7e0611c75551d366b033ac65ee73a0a8984a2b703a7cd52e"'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
    post:
      operationId: createIntegrationPartner
      tags:
        - Partners
      summary: Create a trading partner
      x-required-scope: configuration:write
      description: |
        **Required key scope:** `configuration:write`.

        Creates a tenant-scoped partner. Partner names are unique within the workspace. For every partner attached to an AS2 connection, including carbon-copy-only partners, production and test X12 ISA identifiers must each be unique on that connection. `as2ConnectionId` may be `null` while maps are authored before the partner's connection sheet is available; detached drafts may omit or leave X12 identities incomplete. A connected partner's final configuration must use an ISA qualifier of exactly 2 characters and ISA and GS identifiers between 1 and 15 characters inclusive in both environments. Invalid connected identities return `422 partner_x12_identity_invalid` without creating the partner. `signingCertificateSlot` may be `active` or `next`; `next` is accepted only after a rollover certificate exists.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      requestBody:
        required: true
        description: Partner configuration bodies may be at most 1 MiB (1,048,576 bytes).
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PartnerWriteRequest'
            example:
              name: Retailer One
              as2ConnectionId: null
              x12:
                production:
                  isaQualifier: ZZ
                  isaId: RETAILERONE
                  gsId: RETAILERONE
                test:
                  isaQualifier: ZZ
                  isaId: RETAILERONET
                  gsId: RETAILERONET
              ediSupportEmail: edi@retailer.example
              carbonCopyOnly: false
              unmappedInboundPolicy: MAPPED_ONLY
              signingCertificateSlot: active
              supportsNewlineAfterSegmentTerminator: false
      responses:
        '201':
          description: The partner was created. The response `ETag` matches `partner.etag`; inspect `runtimePublication` to determine whether the committed change is already active in the tenant runtime.
          headers:
            ETag:
              $ref: '#/components/headers/ConfigurationEtag'
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartnerMutationResponse'
        '400':
          $ref: '#/components/responses/InvalidRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '409':
          $ref: '#/components/responses/ConfigurationConflictError'
        '413':
          $ref: '#/components/responses/ConfigurationMutationTooLargeError'
        '422':
          $ref: '#/components/responses/PartnerX12IdentityValidationError'
  /v1/partners/{partnerId}:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
      - name: partnerId
        in: path
        required: true
        description: Tenant-scoped partner id.
        schema:
          type: integer
          minimum: 1
    get:
      operationId: getIntegrationPartner
      tags:
        - Partners
      summary: Get a trading partner
      x-required-scope: configuration:read
      description: |
        **Required key scope:** `configuration:read`.

        Returns the complete active public partner configuration. Retired partners return `404`; their retained rows are used only for historical transaction authorization. The response never exposes internal header overrides or infrastructure settings. Save the `ETag` header or `partner.etag` value before updating or retiring the partner. The partner ETag hashes only mutable partner configuration; derived AS2 readiness fields are excluded because the referenced AS2 connection has its own independently versioned ETag.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      responses:
        '200':
          description: The partner was returned.
          headers:
            ETag:
              $ref: '#/components/headers/ConfigurationEtag'
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartnerResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          $ref: '#/components/responses/ConfigurationNotFoundError'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
    patch:
      operationId: patchIntegrationPartner
      tags:
        - Partners
      summary: Update a trading partner
      x-required-scope: configuration:write
      description: |
        **Required key scope:** `configuration:write`.

        Applies the supplied fields and preserves omitted fields. Send the current resource ETag in `If-Match`. Partner-name and per-connection ISA-identifier uniqueness rules are rechecked atomically. X12 identity validation runs against the final merged configuration, not only the fields in the PATCH body: when the result is connected, both environments require an ISA qualifier of exactly 2 characters and ISA and GS identifiers between 1 and 15 characters inclusive. This makes an attach-only PATCH fail with `422 partner_x12_identity_invalid` when a stored draft identity is incomplete or invalid, without changing the partner. Set `as2ConnectionId` to `null` to detach the partner from its AS2 connection without deleting either record.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        description: Partner configuration bodies may be at most 1 MiB (1,048,576 bytes).
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PartnerPatchRequest'
            example:
              as2ConnectionId: 12
              ediSupportEmail: edi-operations@retailer.example
      responses:
        '200':
          description: The partner was updated. Inspect `runtimePublication` to determine whether the committed change is already active in the tenant runtime.
          headers:
            ETag:
              $ref: '#/components/headers/ConfigurationEtag'
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartnerMutationResponse'
        '400':
          $ref: '#/components/responses/InvalidRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          $ref: '#/components/responses/ConfigurationNotFoundError'
        '409':
          $ref: '#/components/responses/ConfigurationConflictError'
        '412':
          $ref: '#/components/responses/EtagMismatchError'
        '413':
          $ref: '#/components/responses/ConfigurationMutationTooLargeError'
        '422':
          $ref: '#/components/responses/PartnerX12IdentityValidationError'
        '428':
          $ref: '#/components/responses/PreconditionRequiredError'
    delete:
      operationId: deleteIntegrationPartner
      tags:
        - Partners
      summary: Retire a trading partner
      x-required-scope: configuration:write
      description: |
        **Required key scope:** `configuration:write`.

        Retires the partner when no mapping still references it. Send the current resource ETag in `If-Match`; delete dependent mappings first when this operation returns `409 configuration_in_use`.

        Retirement removes the partner from active configuration reads, capabilities, runtime routing, and response-only replay without physically deleting its retained row. The retained partner id continues to authorize historical transaction visibility. Replay requires an active partner plus an applicable currently published map, so retired-partner transactions remain readable but are not replayable. The attached AS2 connection remains in use by the retained row. If you intend to delete that connection, detach it with partner PATCH before retirement.

        Retirement remains available when the aggregate runtime configuration already exceeds 12 MiB so configuration can be reduced. The database change commits, but `runtimePublication.state` may remain `pending` until later deletes bring the runtime document below the cap.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          description: The partner was retired. `deletedPartnerId` is retained as the response field name for API compatibility and identifies the retired partner. Inspect `runtimePublication` for runtime activation state.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                type: object
                required: [success, deletedPartnerId, runtimePublication]
                properties:
                  success:
                    type: boolean
                    enum: [true]
                  deletedPartnerId:
                    type: integer
                    description: Id of the partner that was retired.
                  runtimePublication:
                    $ref: '#/components/schemas/RuntimePublication'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          $ref: '#/components/responses/ConfigurationNotFoundError'
        '409':
          $ref: '#/components/responses/ConfigurationInUseError'
        '412':
          $ref: '#/components/responses/EtagMismatchError'
        '428':
          $ref: '#/components/responses/PreconditionRequiredError'
  /v1/partners/{partnerId}/capabilities:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    get:
      operationId: getIntegrationPartnerCapabilities
      tags:
        - Partners
      summary: Inspect a partner's published mapping capabilities
      x-required-scope: configuration:read
      description: >
        **Required key scope:** `configuration:read`.

        Returns the selected partner plus its published incoming and outgoing
        mapping capabilities. Use this before sending to discover the exact X12
        version, functional group, transaction set, source content type, and
        transform type that are currently configured. `productionReady` and
        `testReady` report whether the corresponding AS2 destination endpoint is
        configured; a published mapping can exist before either endpoint is ready.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - name: partnerId
          in: path
          required: true
          description: Tenant-scoped partner id returned by `GET /v1/partners`.
          schema:
            type: integer
      responses:
        '200':
          description: Partner capabilities were returned.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartnerCapabilitiesResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          description: The partner was not found in this tenant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
  /v1/as2/profile:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    get:
      operationId: getIntegrationAs2Profile
      tags:
        - Partners
      summary: Get the tenant's public AS2 profile
      x-required-scope: configuration:read
      description: >
        **Required key scope:** `configuration:read`.

        Returns the safe connection sheet your trading partners need: production
        and test AS2 URLs and identifiers, X12 sender identities, static network
        addresses included in the current plan, and active or next public
        certificates. Private keys, secret ARNs, billing contacts, and internal
        infrastructure identifiers are never returned. Network capability status
        is `ready`, `pending`, or `not_included`.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      responses:
        '200':
          description: The tenant's AS2 profile was returned.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/As2ProfileResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          description: No AS2 profile was found for the authenticated tenant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
  /v1/as2/connections:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    get:
      operationId: listIntegrationAs2Connections
      tags:
        - AS2 Connections
      summary: List partner AS2 connections
      x-required-scope: configuration:read
      description: |
        **Required key scope:** `configuration:read`.

        Returns every partner AS2 connection in this tenant. Certificates are partner public X.509 certificates formatted as PEM. Private keys and secret locations are never returned.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      responses:
        '200':
          description: AS2 connections were returned.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/As2ConnectionListResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
    post:
      operationId: createIntegrationAs2Connection
      tags:
        - AS2 Connections
      summary: Create a partner AS2 connection
      x-required-scope: configuration:write
      description: |
        **Required key scope:** `configuration:write`.

        Creates transport configuration for a partner's production and test endpoints. Production and test AS2 identifiers must each be unique across this tenant's AS2 connections. Both environments require an AS2 identifier, an absolute public HTTP or HTTPS endpoint URL without user information or a fragment, and one PEM-encoded X.509 public certificate. Endpoint is HTTPS-only; plain HTTP partner URLs require Endpoint Plus, Static Outbound, Static Network, or Enterprise. Omitted transport settings use ModernEDI defaults.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl -X POST "https://api.modernedi.com/v1/as2/connections" \
              -H "Content-Type: application/json" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY" \
              --data @examples/as2-connection-create-request.json
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      requestBody:
        required: true
        description: AS2 connection configuration bodies may be at most 1 MiB (1,048,576 bytes).
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/As2ConnectionWriteRequest'
      responses:
        '201':
          description: The AS2 connection was created. The response `ETag` matches `connection.etag`; inspect `runtimePublication` to determine whether the committed change is already active in the tenant runtime.
          headers:
            ETag:
              $ref: '#/components/headers/ConfigurationEtag'
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/As2ConnectionMutationResponse'
        '400':
          $ref: '#/components/responses/InvalidRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '409':
          $ref: '#/components/responses/ConfigurationConflictError'
        '413':
          $ref: '#/components/responses/ConfigurationMutationTooLargeError'
        '422':
          $ref: '#/components/responses/PlanEntitlementRequiredError'
  /v1/as2/connections/{connectionId}:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
      - name: connectionId
        in: path
        required: true
        description: Tenant-scoped AS2 connection id.
        schema:
          type: integer
          minimum: 1
    get:
      operationId: getIntegrationAs2Connection
      tags:
        - AS2 Connections
      summary: Get a partner AS2 connection
      x-required-scope: configuration:read
      description: |
        **Required key scope:** `configuration:read`.

        Returns one public AS2 connection configuration and its concurrency-control ETag.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      responses:
        '200':
          description: The AS2 connection was returned.
          headers:
            ETag:
              $ref: '#/components/headers/ConfigurationEtag'
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/As2ConnectionResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          $ref: '#/components/responses/ConfigurationNotFoundError'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
    patch:
      operationId: patchIntegrationAs2Connection
      tags:
        - AS2 Connections
      summary: Update a partner AS2 connection
      x-required-scope: configuration:write
      description: |
        **Required key scope:** `configuration:write`.

        Applies the supplied nested fields and preserves omitted fields. Send the current resource ETag in `If-Match`. Production and test AS2 identifier uniqueness is rechecked atomically. The resulting production and test URLs must remain HTTPS unless the workspace has Endpoint Plus, Static Outbound, Static Network, or Enterprise plain-HTTP entitlement.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        description: AS2 connection configuration bodies may be at most 1 MiB (1,048,576 bytes).
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/As2ConnectionPatchRequest'
            example:
              environments:
                test:
                  as2Identifier: RETAILER_TEST_UPDATED
      responses:
        '200':
          description: The AS2 connection was updated. Inspect `runtimePublication` to determine whether the committed change is already active in the tenant runtime.
          headers:
            ETag:
              $ref: '#/components/headers/ConfigurationEtag'
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/As2ConnectionMutationResponse'
        '400':
          $ref: '#/components/responses/InvalidRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          $ref: '#/components/responses/ConfigurationNotFoundError'
        '409':
          $ref: '#/components/responses/ConfigurationConflictError'
        '412':
          $ref: '#/components/responses/EtagMismatchError'
        '413':
          $ref: '#/components/responses/ConfigurationMutationTooLargeError'
        '422':
          $ref: '#/components/responses/PlanEntitlementRequiredError'
        '428':
          $ref: '#/components/responses/PreconditionRequiredError'
    delete:
      operationId: deleteIntegrationAs2Connection
      tags:
        - AS2 Connections
      summary: Delete a partner AS2 connection
      x-required-scope: configuration:write
      description: |
        **Required key scope:** `configuration:write`.

        Deletes the connection when no active or retained partner is assigned to it. Send the current resource ETag in `If-Match`; detach referencing partner records before retirement when this operation returns `409 configuration_in_use`. Delete remains available when the aggregate runtime configuration already exceeds 12 MiB so configuration can be reduced; `runtimePublication.state` may remain `pending` until the document is below the cap.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          description: The AS2 connection was deleted. Inspect `runtimePublication` for runtime activation state.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                type: object
                required: [success, deletedConnectionId, runtimePublication]
                properties:
                  success:
                    type: boolean
                    enum: [true]
                  deletedConnectionId:
                    type: integer
                  runtimePublication:
                    $ref: '#/components/schemas/RuntimePublication'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          $ref: '#/components/responses/ConfigurationNotFoundError'
        '409':
          $ref: '#/components/responses/ConfigurationInUseError'
        '412':
          $ref: '#/components/responses/EtagMismatchError'
        '428':
          $ref: '#/components/responses/PreconditionRequiredError'
  /v1/as2/preview:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    post:
      operationId: previewOutboundX12
      tags:
        - Outbound AS2
      summary: Transform and validate an outbound document without sending it
      x-required-scope: messages:write
      description: >
        **Required key scope:** `messages:write`.

        Uses the same partner, X12 metadata, content type, source body, params,
        and business-key contract as `/v1/as2/send`, but stops after mapping and
        X12 validation. It returns generated X12, validation details, mapping
        provenance, the resolved partner, and the optional business key. It does
        not contact the trading partner, create an outbound transaction, or
        require the partner's AS2 endpoint to be configured. Use it to validate
        a production-shaped request before the first live send.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl -X POST "https://api.modernedi.com/v1/as2/preview?partnerId=1&x12Version=4010&functionalGroupType=IN&transactionGroupType=810" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY" \
              -H "Content-Type: application/vnd.modernedi.outbound+json" \
              --data-binary @invoice-envelope.json
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/OutboundCustomer'
        - $ref: '#/components/parameters/OutboundPartnerId'
        - $ref: '#/components/parameters/OutboundX12Version'
        - $ref: '#/components/parameters/OutboundFunctionalGroupType'
        - $ref: '#/components/parameters/OutboundTransactionGroupType'
      requestBody:
        $ref: '#/components/requestBodies/OutboundMappedSourceBody'
      responses:
        '200':
          description: The source document was mapped and the generated X12 passed validation.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OutboundPreviewResponse'
        '400':
          description: The request was invalid, the transform failed, or generated X12 failed validation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          description: No outgoing map matched the supplied partner, metadata, and content type.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          $ref: '#/components/responses/PayloadTooLargeError'
  /v1/as2/send:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    post:
      operationId: sendAs2Message
      tags:
        - Outbound AS2
      summary: Send a standalone outbound document
      x-required-scope: messages:write
      description: >
        **Required key scope:** `messages:write`.

        Sends a standalone outbound document to the configured trading partner.
        Send the source XML, JSON, or text input as the HTTP request body.
        ModernEDI looks up the matching outgoing map by partner, X12 version,
        functional group, transaction set, and request `Content-Type`, applies
        that map to generate X12, syntax-checks the generated X12, then sends it
        through the configured AS2 connection. JSON source maps can use XSLT
        with Saxon `json-to-xml($json)` or JSLT. For JSLT, ModernEDI sets
        the current input `.` to a wrapper object, so the source document is
        read through `.input` paths. Send either `customer` or `partnerId` to
        select the partner; use `GET /v1/partners` when your integration client
        needs to discover stable partner ids.
        If the outgoing map needs extra runtime parameters such as a
        bill-of-lading number, pallet list, or generated control number, send
        the JSON envelope shape with `input`, `contentType`, and `params`.
        The envelope can also include `businessKey` when your system knows a
        stable purchase order, shipment, BOL, or other business identifier that
        should appear on the outbound transaction record.
        XSLT maps receive those params as top-level stylesheet parameters;
        JSLT maps receive them under `.params`. Mapper-editor params files are
        test fixtures only; live API calls must provide required params in this
        request envelope.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl -X POST "https://api.modernedi.com/v1/as2/send?customer=Partner%20One&x12Version=4010&functionalGroupType=PO&transactionGroupType=850" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY" \
              -H "Content-Type: application/json" \
              -H "Idempotency-Key: send-po-demo-1001" \
              --data '{
                "contentType": "application/xml",
                "input": "<PurchaseOrder><orderNumber>PO-DEMO-1001</orderNumber><orderDate>20260520</orderDate><shipTo><id>DC-DEMO</id></shipTo></PurchaseOrder>",
                "businessKey": {
                  "name": "purchaseOrderNumber",
                  "value": "PO-DEMO-1001"
                },
                "params": {
                  "transactionSetControlNumber": "000002321",
                  "buyerContact": {
                    "name": "Demo Buyer",
                    "email": "buyer@example.com"
                  },
                  "orderNotes": [
                    "Deliver during receiving hours",
                    "Use buyer routing guide"
                  ]
                }
              }'
        - lang: Python
          label: Python
          source: |
            import os
            import requests

            response = requests.post(
                "https://api.modernedi.com/v1/as2/send",
                params={
                    "customer": "Partner One",
                    "x12Version": "4010",
                    "functionalGroupType": "PO",
                    "transactionGroupType": "850",
                },
                headers={
                    "x-api-key": os.environ["MODERNEDI_INTEGRATION_API_KEY"],
                    "Content-Type": "application/json",
                    "Idempotency-Key": "send-po-demo-1001",
                },
                json={
                    "contentType": "application/xml",
                    "input": "<PurchaseOrder><orderNumber>PO-DEMO-1001</orderNumber><orderDate>20260520</orderDate><shipTo><id>DC-DEMO</id></shipTo></PurchaseOrder>",
                    "businessKey": {
                        "name": "purchaseOrderNumber",
                        "value": "PO-DEMO-1001",
                    },
                    "params": {
                        "transactionSetControlNumber": "000002321",
                        "buyerContact": {
                            "name": "Demo Buyer",
                            "email": "buyer@example.com",
                        },
                        "orderNotes": [
                            "Deliver during receiving hours",
                            "Use buyer routing guide",
                        ],
                    },
                },
                timeout=30,
            )
            response.raise_for_status()
        - lang: JavaScript
          label: Node.js
          source: |
            const url = new URL("https://api.modernedi.com/v1/as2/send")
            url.search = new URLSearchParams({
              customer: "Partner One",
              x12Version: "4010",
              functionalGroupType: "PO",
              transactionGroupType: "850",
            })

            const response = await fetch(url, {
              method: "POST",
              headers: {
                "x-api-key": process.env.MODERNEDI_INTEGRATION_API_KEY,
                "Content-Type": "application/json",
                "Idempotency-Key": "send-po-demo-1001",
              },
              body: JSON.stringify({
                contentType: "application/xml",
                input: "<PurchaseOrder><orderNumber>PO-DEMO-1001</orderNumber><orderDate>20260520</orderDate><shipTo><id>DC-DEMO</id></shipTo></PurchaseOrder>",
                businessKey: {
                  name: "purchaseOrderNumber",
                  value: "PO-DEMO-1001",
                },
                params: {
                  transactionSetControlNumber: "000002321",
                  buyerContact: {
                    name: "Demo Buyer",
                    email: "buyer@example.com",
                  },
                  orderNotes: [
                    "Deliver during receiving hours",
                    "Use buyer routing guide",
                  ],
                },
              }),
            })
            if (!response.ok) {
              throw new Error(await response.text())
            }
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/TestMode'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/OutboundCustomer'
        - $ref: '#/components/parameters/OutboundPartnerId'
        - $ref: '#/components/parameters/OutboundX12Version'
        - $ref: '#/components/parameters/OutboundFunctionalGroupType'
        - $ref: '#/components/parameters/OutboundTransactionGroupType'
      requestBody:
        required: true
        content:
          application/xml:
            schema:
              type: string
            example: |
              <PurchaseOrder>
                <orderNumber>PO-DEMO-1001</orderNumber>
                <orderDate>20260520</orderDate>
                <department code="D12">Demo Department</department>
                <shipTo>
                  <id>DC-DEMO</id>
                  <name>Demo Distribution Center</name>
                </shipTo>
              </PurchaseOrder>
          application/vnd.modernedi.outbound+json:
            schema:
              $ref: '#/components/schemas/OutboundTransformEnvelope'
          application/json:
            schema:
              anyOf:
                - type: object
                  additionalProperties: true
                - $ref: '#/components/schemas/OutboundTransformEnvelope'
                - type: array
                  items: {}
                - type: string
                  nullable: true
                - type: number
                - type: boolean
            examples:
              jsonSourceInput:
                summary: JSON source input
                value:
                  orderNumber: PO-DEMO-1001
                  orderDate: '20260520'
                  department:
                    code: D12
                    name: Demo Department
                  shipTo:
                    id: DC-DEMO
                    name: Demo Distribution Center
                  lines:
                    - position: 1
                      quantity: 24
                      unitOfMeasure: EA
                      unitPrice: '10.00'
                      buyerItemNumber: DEMO-BUY-001
                      description: Demo Item Alpha
              xmlSourceWithParams:
                summary: XML source input plus transform params
                value:
                  contentType: application/xml
                  input: "<PurchaseOrder><orderNumber>PO-DEMO-1001</orderNumber></PurchaseOrder>"
                  businessKey:
                    name: purchaseOrderNumber
                    value: PO-DEMO-1001
                  params:
                    transactionSetControlNumber: '000002321'
                    buyerContact:
                      name: Demo Buyer
                      email: buyer@example.com
                    orderNotes:
                      - Deliver during receiving hours
                      - Use buyer routing guide
          text/plain:
            schema:
              type: string
            example: PO-DEMO-1001|20260520|D12|Demo Distribution Center
      responses:
        '200':
          description: >
            Outbound AS2 message accepted, transformed into X12, and sent. The
            `x12` field is generated output returned for visibility; request
            bodies remain XML, JSON, text, or the transform envelope.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
            Idempotency-Replayed:
              $ref: '#/components/headers/IdempotencyReplayed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendSuccessResponse'
              example:
                success: true
                messageId: <generated-message-id@modernedi>
                x12: ISA*00*          *00*          *ZZ*SENDERID       *ZZ*RECEIVERID     *260427*2100*U*00401*000000001*0*T*>~GS*PO*SENDER*RECEIVER*20260427*2100*1*X*004010~ST*850*000000001~BEG*00*SA*PO-DEMO-1001**20260520~REF*DP*D12~N1*ST*Demo Distribution Center*92*DC-DEMO~PO1*1*24*EA*10.00**IN*DEMO-BUY-001~CTT*1~SE*6*000000001~GE*1*1~IEA*1*000000001~
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '409':
          description: The Idempotency-Key is already in progress or was reused for a different request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error:
                  code: idempotency_key_conflict
                  message: This Idempotency-Key was already used for a different request body or operation.
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details: {}
        '400':
          description: >
            The source input could not be transformed, or the generated X12
            failed syntax validation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error:
                  code: invalid_generated_x12
                  message: Outgoing map generated invalid X12.
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details: {}
        '404':
          description: No outgoing map matched the supplied partner, transaction metadata, and content type.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error:
                  code: not_found
                  message: No outgoing transform map matched customer "Partner One", x12Version "4010", functionalGroupType "PO", transactionGroupType "850", and contentType "application/xml".
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details: {}
        '413':
          $ref: '#/components/responses/PayloadTooLargeError'
        '429':
          $ref: '#/components/responses/TenantAs2RateLimitError'
        '502':
          description: The generated X12 could not be delivered to the tenant AS2 runtime.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error:
                  code: tenant_as2_send_failed
                  message: The tenant AS2 runtime rejected the outbound message.
                  retryable: true
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details:
                    tenantAs2StatusCode: 502
        default:
          description: >
            A non-2xx status was returned by the downstream partner AS2 endpoint
            and was forwarded back to the caller.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/as2/reply:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    post:
      operationId: replyToInboundAs2Message
      tags:
        - Outbound AS2
      summary: Send a follow-up document tied to an inbound message
      x-required-scope: messages:write
      description: >
        **Required key scope:** `messages:write`.

        Sends a follow-up document such as an ASN or invoice in response to a
        previously received inbound message. Send the source XML, JSON, or text
        input as the HTTP request body. ModernEDI looks up the original
        message by `originalMessageId`, and uses unambiguous metadata from that
        inbound transaction to recover an omitted partner selector and
        `x12Version`. This lookup uses production transaction history by default;
        send `test=true` to infer from and link to isolated test history.
        ModernEDI uses the persisted `partnerId`, so the reply
        still selects the same partner after its configured name changes.
        Transactions without a persisted partner id do not supply inferred
        partner metadata. An explicit `partnerId` or `customer` always overrides inferred
        partner metadata. If persisted partner ids conflict, ModernEDI does not
        infer either partner selector. After the partner selector, X12 version, and request
        `Content-Type` are known, ModernEDI can
        also fill omitted `functionalGroupType` and `transactionGroupType` when
        those selectors match exactly one outgoing reply map. ModernEDI then
        selects the matching outgoing map by partner, X12 version, functional
        group, transaction set, and request `Content-Type`, generates and
        validates X12, then sends the reply through the configured AS2
        connection.
        Send `x12Version` explicitly when a partner requires reply documents on
        a different X12 version than the one they send; explicit query parameters
        override inferred metadata.
        If multiple reply maps could match, send `functionalGroupType` and
        `transactionGroupType` explicitly. The original inbound document type is
        not reused as the reply type; these fields identify the generated reply
        document, such as `IN`/`810` for an invoice. JSON source maps can use XSLT
        with Saxon `json-to-xml($json)` or JSLT. For JSLT, ModernEDI sets
        the current input `.` to a wrapper object, so the source document is
        read through `.input` paths.
        If the outgoing map needs extra runtime parameters, send the JSON
        envelope shape with `input`, `contentType`, and `params`; XSLT maps
        receive those values as top-level stylesheet parameters, JSLT maps read
        them from `.params`, and mapper-editor params files are not used as live
        defaults. Use the envelope's optional `businessKey` field when the reply
        has a stable identifier, such as an invoice number, that should be stored
        on the outbound transaction record.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl -X POST "https://api.modernedi.com/v1/as2/reply?originalMessageId=%3Cinbound-message-id%40partner%3E" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY" \
              -H "Content-Type: application/json" \
              -H "Idempotency-Key: reply-invoice-demo-1001" \
              --data '{
                "contentType": "application/xml",
                "input": "<PurchaseOrder><orderNumber>PO-DEMO-1001</orderNumber><invoice><number>INV-DEMO-1001</number><date>20260525</date></invoice><totals><amount>240.00</amount></totals></PurchaseOrder>",
                "businessKey": {
                  "name": "invoiceNumber",
                  "value": "INV-DEMO-1001"
                },
                "params": {
                  "invoiceDate": "20260525",
                  "termsCode": "05",
                  "termDays": 30
                }
              }'
        - lang: Python
          label: Python
          source: |
            import os
            import requests

            response = requests.post(
                "https://api.modernedi.com/v1/as2/reply",
                params={
                    "originalMessageId": "<inbound-message-id@partner>",
                },
                headers={
                    "x-api-key": os.environ["MODERNEDI_INTEGRATION_API_KEY"],
                    "Content-Type": "application/json",
                    "Idempotency-Key": "reply-invoice-demo-1001",
                },
                json={
                    "contentType": "application/xml",
                    "input": "<PurchaseOrder><orderNumber>PO-DEMO-1001</orderNumber><invoice><number>INV-DEMO-1001</number><date>20260525</date></invoice><totals><amount>240.00</amount></totals></PurchaseOrder>",
                    "businessKey": {
                        "name": "invoiceNumber",
                        "value": "INV-DEMO-1001",
                    },
                    "params": {
                        "invoiceDate": "20260525",
                        "termsCode": "05",
                        "termDays": 30,
                    },
                },
                timeout=30,
            )
            response.raise_for_status()
        - lang: JavaScript
          label: Node.js
          source: |
            const url = new URL("https://api.modernedi.com/v1/as2/reply")
            url.search = new URLSearchParams({
              originalMessageId: "<inbound-message-id@partner>",
            })

            const response = await fetch(url, {
              method: "POST",
              headers: {
                "x-api-key": process.env.MODERNEDI_INTEGRATION_API_KEY,
                "Content-Type": "application/json",
                "Idempotency-Key": "reply-invoice-demo-1001",
              },
              body: JSON.stringify({
                contentType: "application/xml",
                input: "<PurchaseOrder><orderNumber>PO-DEMO-1001</orderNumber><invoice><number>INV-DEMO-1001</number><date>20260525</date></invoice><totals><amount>240.00</amount></totals></PurchaseOrder>",
                businessKey: {
                  name: "invoiceNumber",
                  value: "INV-DEMO-1001",
                },
                params: {
                  invoiceDate: "20260525",
                  termsCode: "05",
                  termDays: 30,
                },
              }),
            })
            if (!response.ok) {
              throw new Error(await response.text())
            }
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/TestMode'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/ReplyOutboundCustomer'
        - $ref: '#/components/parameters/OutboundPartnerId'
        - $ref: '#/components/parameters/ReplyOutboundX12Version'
        - $ref: '#/components/parameters/ReplyOutboundFunctionalGroupType'
        - $ref: '#/components/parameters/ReplyOutboundTransactionGroupType'
        - $ref: '#/components/parameters/OriginalMessageId'
      requestBody:
        required: true
        content:
          application/xml:
            schema:
              type: string
            example: |
              <PurchaseOrder>
                <orderNumber>PO-DEMO-1001</orderNumber>
                <invoice>
                  <number>INV-DEMO-1001</number>
                  <date>20260525</date>
                </invoice>
                <totals>
                  <amount>240.00</amount>
                </totals>
              </PurchaseOrder>
          application/vnd.modernedi.outbound+json:
            schema:
              $ref: '#/components/schemas/OutboundTransformEnvelope'
          application/json:
            schema:
              anyOf:
                - type: object
                  additionalProperties: true
                - $ref: '#/components/schemas/OutboundTransformEnvelope'
                - type: array
                  items: {}
                - type: string
                  nullable: true
                - type: number
                - type: boolean
            examples:
              jsonSourceInput:
                summary: JSON source input
                value:
                  orderNumber: PO-DEMO-1001
                  invoice:
                    number: INV-DEMO-1001
                    date: '20260525'
                  totals:
                    amount: '240.00'
              xmlSourceWithParams:
                summary: XML source input plus transform params
                value:
                  contentType: application/xml
                  input: "<PurchaseOrder><orderNumber>PO-DEMO-1001</orderNumber><invoice><number>INV-DEMO-1001</number><date>20260525</date></invoice></PurchaseOrder>"
                  businessKey:
                    name: invoiceNumber
                    value: INV-DEMO-1001
                  params:
                    invoiceDate: '20260525'
                    termsCode: '05'
                    termDays: 30
          text/plain:
            schema:
              type: string
            example: PO-DEMO-1001|INV-DEMO-1001|20260525|240.00
      responses:
        '200':
          description: >
            Outbound reply accepted, transformed into X12, and sent. The `x12`
            field is generated output returned for visibility; request bodies
            remain XML, JSON, text, or the transform envelope.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
            Idempotency-Replayed:
              $ref: '#/components/headers/IdempotencyReplayed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendSuccessResponse'
              example:
                success: true
                messageId: <generated-message-id@modernedi>
                x12: ISA*00*          *00*          *ZZ*SENDERID       *ZZ*RECEIVERID     *260427*2100*U*00401*000000001*0*T*>~GS*IN*SENDER*RECEIVER*20260427*2100*1*X*004010~ST*810*000000001~BIG*20260427*INV-DEMO-1001*20260420*PO-DEMO-1001~IT1*1*24*EA*10.00**IN*DEMO-BUY-001~TDS*24000~SE*5*000000001~GE*1*1~IEA*1*000000001~
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '409':
          description: The Idempotency-Key is already in progress or was reused for a different request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error:
                  code: idempotency_key_conflict
                  message: This Idempotency-Key was already used for a different request body or operation.
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details: {}
        '400':
          description: >
            The source input could not be transformed, or the generated X12
            failed syntax validation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error:
                  code: invalid_generated_x12
                  message: Outgoing map generated invalid X12.
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details: {}
        '404':
          description: No outgoing map matched the supplied partner, transaction metadata, and content type.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error:
                  code: not_found
                  message: No outgoing transform map matched customer "Partner One", x12Version "4010", functionalGroupType "IN", transactionGroupType "810", and contentType "application/xml".
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details: {}
        '413':
          $ref: '#/components/responses/PayloadTooLargeError'
        '429':
          $ref: '#/components/responses/TenantAs2RateLimitError'
        '502':
          description: The generated X12 could not be delivered to the tenant AS2 runtime.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error:
                  code: tenant_as2_send_failed
                  message: The tenant AS2 runtime rejected the outbound message.
                  retryable: true
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details:
                    tenantAs2StatusCode: 502
        default:
          description: >
            A non-2xx status was returned by the downstream partner AS2 endpoint
            and was forwarded back to the caller.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/as2/carbonCopy:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    post:
      operationId: sendAs2CarbonCopy
      tags:
        - Outbound AS2
      summary: Send a carbon copy of an inbound message flow to another partner
      x-required-scope: messages:write
      description: >
        **Required key scope:** `messages:write`.

        Sends a generated X12 document to a second partner while preserving
        context from a previously received inbound message. Send the source XML,
        JSON, or text input as the HTTP request body. The `customer` or
        `partnerId` query parameter identifies the source/original partner whose
        outgoing map should generate the X12, and `copyToCustomer` or
        `copyToPartnerId` identifies the destination partner for the copied AS2
        envelope. ModernEDI selects the matching
        outgoing map for that original/source partner, generates and validates X12,
        then forwards the carbon copy through the configured AS2 connection.
        Example: to generate an 856 ASN for Retailer One and copy it to Broker One,
        send `customer=Retailer%20One` and `copyToPartnerId=2`; ModernEDI uses
        Retailer One's outgoing 856 map. `copyToCustomer` does not select the
        outgoing map, and neither does `copyToPartnerId`. JSON source maps can use XSLT with Saxon
        `json-to-xml($json)` or JSLT. For JSLT, ModernEDI sets the current
        input `.` to a wrapper object, so the source document is read through
        `.input` paths. If the
        outgoing map needs extra runtime parameters, send the JSON envelope
        shape with `input`, `contentType`, and `params`; the map is still
        selected by `customer`/`partnerId`, not by `copyToCustomer` or
        `copyToPartnerId`. XSLT maps
        receive params as top-level stylesheet parameters, JSLT maps read them
        from `.params`, and mapper-editor params files are test fixtures only
        and are not read by live carbon-copy calls. The envelope's optional
        `businessKey` is recorded on the copied outbound transaction, but it
        does not affect map selection.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl -X POST "https://api.modernedi.com/v1/as2/carbonCopy?customer=Retailer%20One&copyToPartnerId=2&x12Version=4010&functionalGroupType=SH&transactionGroupType=856&originalMessageId=msg-850-api" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY" \
              -H "Content-Type: application/json" \
              -H "Idempotency-Key: carbon-copy-asn-demo-1001" \
              --data '{
                "contentType": "application/xml",
                "input": "<PurchaseOrder><orderNumber>PO-DEMO-1001</orderNumber><shipment><id>ASN-DEMO-1001</id><date>20260526</date></shipment></PurchaseOrder>",
                "businessKey": {
                  "name": "shipmentId",
                  "value": "ASN-DEMO-1001"
                },
                "params": {
                  "bolNumber": "BOL-DEMO-8842",
                  "totalWeightInLbs": 1232.54,
                  "transactionSetControlNumber": "000002321"
                }
              }'
        - lang: Python
          label: Python
          source: |
            import os
            import requests

            response = requests.post(
                "https://api.modernedi.com/v1/as2/carbonCopy",
                params={
                    "customer": "Retailer One",
                    "copyToPartnerId": 2,
                    "x12Version": "4010",
                    "functionalGroupType": "SH",
                    "transactionGroupType": "856",
                    "originalMessageId": "msg-850-api",
                },
                headers={
                    "x-api-key": os.environ["MODERNEDI_INTEGRATION_API_KEY"],
                    "Content-Type": "application/json",
                    "Idempotency-Key": "carbon-copy-asn-demo-1001",
                },
                json={
                    "contentType": "application/xml",
                    "input": "<PurchaseOrder><orderNumber>PO-DEMO-1001</orderNumber><shipment><id>ASN-DEMO-1001</id><date>20260526</date></shipment></PurchaseOrder>",
                    "businessKey": {
                        "name": "shipmentId",
                        "value": "ASN-DEMO-1001",
                    },
                    "params": {
                        "bolNumber": "BOL-DEMO-8842",
                        "totalWeightInLbs": 1232.54,
                        "transactionSetControlNumber": "000002321",
                    },
                },
                timeout=30,
            )
            response.raise_for_status()
        - lang: JavaScript
          label: Node.js
          source: |
            const url = new URL("https://api.modernedi.com/v1/as2/carbonCopy")
            url.search = new URLSearchParams({
              customer: "Retailer One",
              copyToPartnerId: "2",
              x12Version: "4010",
              functionalGroupType: "SH",
              transactionGroupType: "856",
              originalMessageId: "msg-850-api",
            })

            const response = await fetch(url, {
              method: "POST",
              headers: {
                "x-api-key": process.env.MODERNEDI_INTEGRATION_API_KEY,
                "Content-Type": "application/json",
                "Idempotency-Key": "carbon-copy-asn-demo-1001",
              },
              body: JSON.stringify({
                contentType: "application/xml",
                input: "<PurchaseOrder><orderNumber>PO-DEMO-1001</orderNumber><shipment><id>ASN-DEMO-1001</id><date>20260526</date></shipment></PurchaseOrder>",
                businessKey: {
                  name: "shipmentId",
                  value: "ASN-DEMO-1001",
                },
                params: {
                  bolNumber: "BOL-DEMO-8842",
                  totalWeightInLbs: 1232.54,
                  transactionSetControlNumber: "000002321",
                },
              }),
            })
            if (!response.ok) {
              throw new Error(await response.text())
            }
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/TestMode'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/OutboundCustomer'
        - $ref: '#/components/parameters/OutboundPartnerId'
        - $ref: '#/components/parameters/OutboundX12Version'
        - $ref: '#/components/parameters/OutboundFunctionalGroupType'
        - $ref: '#/components/parameters/OutboundTransactionGroupType'
        - $ref: '#/components/parameters/OriginalMessageId'
        - $ref: '#/components/parameters/CopyToCustomer'
        - $ref: '#/components/parameters/CopyToPartnerId'
      requestBody:
        required: true
        content:
          application/xml:
            schema:
              type: string
            example: |
              <PurchaseOrder>
                <orderNumber>PO-DEMO-1001</orderNumber>
                <shipment>
                  <id>ASN-DEMO-1001</id>
                  <date>20260526</date>
                  <time>1015</time>
                  <billOfLading>BOL-DEMO-1001</billOfLading>
                </shipment>
              </PurchaseOrder>
          application/vnd.modernedi.outbound+json:
            schema:
              $ref: '#/components/schemas/OutboundTransformEnvelope'
          application/json:
            schema:
              anyOf:
                - type: object
                  additionalProperties: true
                - $ref: '#/components/schemas/OutboundTransformEnvelope'
                - type: array
                  items: {}
                - type: string
                  nullable: true
                - type: number
                - type: boolean
            examples:
              jsonSourceInput:
                summary: JSON source input
                value:
                  orderNumber: PO-DEMO-1001
                  shipment:
                    id: ASN-DEMO-1001
                    date: '20260526'
                    time: '1015'
                    billOfLading: BOL-DEMO-1001
              xmlSourceWithParams:
                summary: XML source input plus transform params
                value:
                  contentType: application/xml
                  input: "<PurchaseOrder><orderNumber>PO-DEMO-1001</orderNumber><shipment><id>ASN-DEMO-1001</id><date>20260526</date><time>1015</time></shipment></PurchaseOrder>"
                  businessKey:
                    name: shipmentId
                    value: ASN-DEMO-1001
                  params:
                    bolNumber: BOL-DEMO-8842
                    totalWeightInLbs: 1232.54
                    transactionSetControlNumber: '000002321'
                    pallets:
                      - buyerSku: DEMO-SKU-001
                        itemNumber: DEMO-ITEM-001
                        numCases: 187
                        sscc18: '000000000000000101'
          text/plain:
            schema:
              type: string
            example: PO-DEMO-1001|ASN-DEMO-1001|20260526|1015|BOL-DEMO-1001
      responses:
        '200':
          description: >
            Outbound carbon copy accepted, transformed into X12, and sent. The
            `x12` field is generated output returned for visibility; request
            bodies remain XML, JSON, text, or the transform envelope.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
            Idempotency-Replayed:
              $ref: '#/components/headers/IdempotencyReplayed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendSuccessResponse'
              example:
                success: true
                messageId: <generated-message-id@modernedi>
                x12: ISA*00*          *00*          *ZZ*SENDERID       *ZZ*RECEIVERID     *260427*2100*U*00401*000000001*0*T*>~GS*SH*SENDER*RECEIVER*20260427*2100*1*X*004010~ST*856*000000001~BSN*00*ASN0001*20260427*2100~HL*1**S~TD5*B*2*UPSN*M*GROUND~REF*CN*1Z999999~HL*2*1*O~PRF*PO0001~SE*8*000000001~GE*1*1~IEA*1*000000001~
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '409':
          description: The Idempotency-Key is already in progress or was reused for a different request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error:
                  code: idempotency_key_conflict
                  message: This Idempotency-Key was already used for a different request body or operation.
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details: {}
        '400':
          description: >
            The source input could not be transformed, or the generated X12
            failed syntax validation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error:
                  code: invalid_generated_x12
                  message: Outgoing map generated invalid X12.
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details: {}
        '404':
          description: No outgoing map matched the supplied partner, transaction metadata, and content type.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error:
                  code: not_found
                  message: No outgoing transform map matched customer "Retailer One", x12Version "4010", functionalGroupType "SH", transactionGroupType "856", and contentType "application/xml".
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details: {}
        '413':
          $ref: '#/components/responses/PayloadTooLargeError'
        '429':
          $ref: '#/components/responses/TenantAs2RateLimitError'
        '502':
          description: The generated X12 could not be delivered to the tenant AS2 runtime.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error:
                  code: tenant_as2_send_failed
                  message: The tenant AS2 runtime rejected the outbound message.
                  retryable: true
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details:
                    tenantAs2StatusCode: 502
        default:
          description: >
            A non-2xx status was returned by the downstream partner AS2 endpoint
            and was forwarded back to the caller.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/as2/x12/send:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    post:
      operationId: sendGeneratedX12Message
      tags:
        - Outbound AS2
      summary: Send an already-generated X12 document
      x-required-scope: messages:write
      description: >
        **Required key scope:** `messages:write`.

        Sends X12 that your system generated without applying a ModernEDI
        outgoing map. ModernEDI parses and validates the supplied X12, builds
        the AS2 message, and delivers it through the selected partner connection.
        Supply the X12 directly with `Content-Type: application/edi-x12` (or
        `text/plain`), or use the JSON envelope when you also want to record a
        business key. The partner selector and X12 metadata remain explicit so
        ModernEDI can choose and audit the correct destination. Use
        `/v1/as2/send` instead when ModernEDI should map your business document
        into X12 first.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl -X POST "https://api.modernedi.com/v1/as2/x12/send?partnerId=1&x12Version=4010&functionalGroupType=PO&transactionGroupType=850" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY" \
              -H "Content-Type: application/edi-x12" \
              -H "Idempotency-Key: raw-po-100045" \
              --data-binary @purchase-order.edi
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/TestMode'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/OutboundCustomer'
        - $ref: '#/components/parameters/OutboundPartnerId'
        - $ref: '#/components/parameters/OutboundX12Version'
        - $ref: '#/components/parameters/OutboundFunctionalGroupType'
        - $ref: '#/components/parameters/OutboundTransactionGroupType'
      requestBody:
        $ref: '#/components/requestBodies/GeneratedX12Body'
      responses:
        '200':
          description: The supplied X12 was validated and sent over AS2.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
            Idempotency-Replayed:
              $ref: '#/components/headers/IdempotencyReplayed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendSuccessResponse'
        '400':
          description: The request or supplied X12 was invalid.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          description: The selected partner was not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: The idempotency key was previously used for a different request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          $ref: '#/components/responses/PayloadTooLargeError'
        '429':
          $ref: '#/components/responses/TenantAs2RateLimitError'
        '502':
          description: The selected tenant AS2 runtime or partner destination was unreachable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        default:
          description: A downstream partner AS2 status was forwarded to the caller.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/as2/x12/reply:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    post:
      operationId: replyWithGeneratedX12
      tags:
        - Outbound AS2
      summary: Reply to an inbound message with already-generated X12
      x-required-scope: messages:write
      description: >
        **Required key scope:** `messages:write`.

        Sends X12 that your system generated as a reply tied to an inbound AS2
        message. ModernEDI does not apply an outgoing map. When the partner or
        X12 version is omitted, `originalMessageId` can recover it from the
        selected transaction history. Production history is the default; send
        `test=true` to infer from and link to isolated test history. The functional group
        and transaction-set identifiers describe the reply document and remain
        required because they are not copied from the original inbound document.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl -X POST "https://api.modernedi.com/v1/as2/x12/reply?originalMessageId=%3Cinbound-message-id%40partner%3E&functionalGroupType=IN&transactionGroupType=810" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY" \
              -H "Content-Type: application/edi-x12" \
              -H "Idempotency-Key: raw-invoice-100045" \
              --data-binary @invoice.edi
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/TestMode'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/ReplyOutboundCustomer'
        - $ref: '#/components/parameters/OutboundPartnerId'
        - $ref: '#/components/parameters/ReplyOutboundX12Version'
        - $ref: '#/components/parameters/OutboundFunctionalGroupType'
        - $ref: '#/components/parameters/OutboundTransactionGroupType'
        - $ref: '#/components/parameters/OriginalMessageId'
      requestBody:
        $ref: '#/components/requestBodies/GeneratedX12Body'
      responses:
        '200':
          description: The supplied X12 reply was validated and sent over AS2.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
            Idempotency-Replayed:
              $ref: '#/components/headers/IdempotencyReplayed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendSuccessResponse'
        '400':
          description: The request or supplied X12 was invalid or reply metadata was ambiguous.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          description: The original message or selected partner was not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: The idempotency key was previously used for a different request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          $ref: '#/components/responses/PayloadTooLargeError'
        '429':
          $ref: '#/components/responses/TenantAs2RateLimitError'
        '502':
          description: The selected tenant AS2 runtime or partner destination was unreachable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        default:
          description: A downstream partner AS2 status was forwarded to the caller.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/as2/x12/carbonCopy:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    post:
      operationId: carbonCopyGeneratedX12
      tags:
        - Outbound AS2
      summary: Send already-generated X12 to a carbon-copy partner
      x-required-scope: messages:write
      description: >
        **Required key scope:** `messages:write`.

        Sends supplied X12 to a second partner while preserving the original
        transaction context. ModernEDI validates the X12 but does not apply an
        outgoing map. Select the original partner with `partnerId` or `customer`
        and the recipient with `copyToPartnerId` or `copyToCustomer`.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl -X POST "https://api.modernedi.com/v1/as2/x12/carbonCopy?partnerId=1&copyToPartnerId=2&originalMessageId=msg-850-api&x12Version=4010&functionalGroupType=SH&transactionGroupType=856" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY" \
              -H "Content-Type: application/edi-x12" \
              -H "Idempotency-Key: raw-asn-copy-100045" \
              --data-binary @shipment.edi
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/TestMode'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/OutboundCustomer'
        - $ref: '#/components/parameters/OutboundPartnerId'
        - $ref: '#/components/parameters/OutboundX12Version'
        - $ref: '#/components/parameters/OutboundFunctionalGroupType'
        - $ref: '#/components/parameters/OutboundTransactionGroupType'
        - $ref: '#/components/parameters/OriginalMessageId'
        - $ref: '#/components/parameters/CopyToCustomer'
        - $ref: '#/components/parameters/CopyToPartnerId'
      requestBody:
        $ref: '#/components/requestBodies/GeneratedX12Body'
      responses:
        '200':
          description: The supplied X12 was validated and sent to the carbon-copy partner.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
            Idempotency-Replayed:
              $ref: '#/components/headers/IdempotencyReplayed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendSuccessResponse'
        '400':
          description: The request or supplied X12 was invalid.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          description: The original transaction or selected partner was not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: The idempotency key was previously used for a different request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          $ref: '#/components/responses/PayloadTooLargeError'
        '429':
          $ref: '#/components/responses/TenantAs2RateLimitError'
        '502':
          description: The selected tenant AS2 runtime or partner destination was unreachable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        default:
          description: A downstream partner AS2 status was forwarded to the caller.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/mappings:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    get:
      operationId: listIntegrationMappings
      tags:
        - Mappings
      summary: List published mappings
      x-required-scope: configuration:read
      description: |
        **Required key scope:** `configuration:read`.

        Returns the tenant's published incoming and outgoing maps, including the full transform source, delivery category, patchability, and each resource ETag. Destination URLs, queue names, table names, and other infrastructure details are never exposed. Legacy mappings that write directly to external SQS or DynamoDB are visible for migration but marked `patchable: false`.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      responses:
        '200':
          description: Mappings were returned.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MappingListResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
    post:
      operationId: createIntegrationMapping
      tags:
        - Mappings
      summary: Publish a mapping
      x-required-scope: configuration:write
      description: |
        **Required key scope:** `configuration:write`.

        Publishes a map and archives its initial revision. Incoming mappings require `direction: INCOMING`, `transform.type: X12_MAPPER`, and `businessKey`; new incoming mappings always deliver through the managed mapped-output polling and webhook lane and cannot name arbitrary SQS or DynamoDB destinations. Outgoing mappings require `direction: OUTGOING`, `transform.type: XSLT` or `JSLT`, and `output.purpose: PROCESSING`. `patchable` and `output.delivery` are response-only fields selected by ModernEDI.

        Before publishing, ModernEDI parses and semantically checks incoming X12 Mapper source and its business-key extractor against the selected X12 version and transaction set. Outgoing XSLT and JSLT source is compiled with the hosted runtime compiler. A failure returns `422 mapping_validation_failed` with field-specific diagnostics and does not create a mapping or revision.

        `Idempotency-Key` is required. Repeating the same tenant-scoped key with the same JSON request returns the existing mapping with `Idempotency-Replayed: true` and does not create another mapping or revision. JSON object field order does not affect request identity. Reusing the key with a different request returns `409 idempotency_key_conflict`. If the originally created mapping was later deleted, reuse returns `409 idempotency_result_unavailable`; use a new key only when you intend to create a new mapping.
      x-codeSamples:
        - lang: curl
          label: Incoming map
          source: |
            curl -X POST "https://api.modernedi.com/v1/mappings" \
              -H "Content-Type: application/json" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY" \
              -H "Idempotency-Key: mapping-850-incoming-v1" \
              --data @examples/mapping-incoming-create-request.json
        - lang: curl
          label: Outgoing map
          source: |
            curl -X POST "https://api.modernedi.com/v1/mappings" \
              -H "Content-Type: application/json" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY" \
              -H "Idempotency-Key: mapping-810-outgoing-v1" \
              --data @examples/mapping-outgoing-create-request.json
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/MappingCreateIdempotencyKey'
      requestBody:
        required: true
        description: Mapping configuration bodies may be at most 1 MiB (1,048,576 bytes). This is a transport ceiling for the complete JSON document, not a guaranteed maximum transform-source size.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MappingWriteRequest'
      responses:
        '201':
          description: The mapping was published in configuration storage. The response `ETag` matches `mapping.etag`; inspect `runtimePublication` to determine whether the committed change is already active in the tenant runtime.
          headers:
            ETag:
              $ref: '#/components/headers/ConfigurationEtag'
            Idempotency-Replayed:
              $ref: '#/components/headers/MappingIdempotencyReplayed'
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MappingMutationResponse'
        '400':
          $ref: '#/components/responses/InvalidRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '409':
          $ref: '#/components/responses/MappingIdempotencyConflictError'
        '413':
          $ref: '#/components/responses/ConfigurationMutationTooLargeError'
        '422':
          $ref: '#/components/responses/MappingValidationError'
  /v1/mappings/{mappingId}:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
      - name: mappingId
        in: path
        required: true
        description: Tenant-scoped mapping id.
        schema:
          type: integer
          minimum: 1
    get:
      operationId: getIntegrationMapping
      tags:
        - Mappings
      summary: Get a published mapping
      x-required-scope: configuration:read
      description: |
        **Required key scope:** `configuration:read`.

        Returns one published map, its complete source text, safe output configuration, delivery category, patchability, and concurrency-control ETag.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      responses:
        '200':
          description: The mapping was returned.
          headers:
            ETag:
              $ref: '#/components/headers/ConfigurationEtag'
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MappingResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          $ref: '#/components/responses/ConfigurationNotFoundError'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
    patch:
      operationId: patchIntegrationMapping
      tags:
        - Mappings
      summary: Update and republish a mapping
      x-required-scope: configuration:write
      description: |
        **Required key scope:** `configuration:write`.

        Deep-merges the supplied fields with the current configuration, validates the complete resulting mapping, archives the prior revision, and publishes it. Send the current resource ETag in `If-Match`. For a source-only deployment, send `{ "transform": { "source": "..." } }`. A mapping returned with `patchable: false` cannot be changed through this endpoint and returns `409 mapping_not_publicly_manageable`; ETag-protected deletion remains available.

        Incoming X12 Mapper source and its business-key extractor are parsed and semantically checked against the selected X12 version and transaction set. Outgoing XSLT and JSLT source is compiled with the hosted runtime compiler. A `422 mapping_validation_failed` response leaves the currently published mapping and revision unchanged.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        description: Mapping configuration bodies may be at most 1 MiB (1,048,576 bytes). This is a transport ceiling for the complete JSON document, not a guaranteed maximum transform-source size.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MappingPatchRequest'
            example:
              transform:
                source: '"{\"purchaseOrderNumber\":\"" + ST->BEG(03) + "\"}"'
      responses:
        '200':
          description: The mapping was updated in configuration storage. Inspect `runtimePublication` to determine whether the committed change is already active in the tenant runtime.
          headers:
            ETag:
              $ref: '#/components/headers/ConfigurationEtag'
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MappingMutationResponse'
        '400':
          $ref: '#/components/responses/InvalidRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          $ref: '#/components/responses/ConfigurationNotFoundError'
        '409':
          $ref: '#/components/responses/MappingNotPubliclyManageableError'
        '412':
          $ref: '#/components/responses/EtagMismatchError'
        '413':
          $ref: '#/components/responses/ConfigurationMutationTooLargeError'
        '422':
          $ref: '#/components/responses/MappingValidationError'
        '428':
          $ref: '#/components/responses/PreconditionRequiredError'
    delete:
      operationId: deleteIntegrationMapping
      tags:
        - Mappings
      summary: Delete a mapping
      x-required-scope: configuration:write
      description: |
        **Required key scope:** `configuration:write`.

        Archives the current revision and deletes the published mapping. Send the current resource ETag in `If-Match`. Delete remains available when the aggregate runtime configuration already exceeds 12 MiB so configuration can be reduced; `runtimePublication.state` may remain `pending` until the document is below the cap.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          description: The mapping was deleted. Inspect `runtimePublication` for runtime activation state.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                type: object
                required: [success, deletedMappingId, runtimePublication]
                properties:
                  success:
                    type: boolean
                    enum: [true]
                  deletedMappingId:
                    type: integer
                  runtimePublication:
                    $ref: '#/components/schemas/RuntimePublication'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          $ref: '#/components/responses/ConfigurationNotFoundError'
        '412':
          $ref: '#/components/responses/EtagMismatchError'
        '428':
          $ref: '#/components/responses/PreconditionRequiredError'
  /v1/mappings/{mappingId}/revisions:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
      - name: mappingId
        in: path
        required: true
        description: Tenant-scoped mapping id.
        schema:
          type: integer
          minimum: 1
    get:
      operationId: listIntegrationMappingSourceVersions
      tags:
        - Mappings
      summary: List a mapping's source history
      x-required-scope: configuration:read
      description: |
        **Required key scope:** `configuration:read`.

        Returns one page of the immutable transform-source versions retained for a published mapping, newest first. Page entries intentionally omit source text; fetch one source version by id to preview it or compute a diff. Follow `nextCursor` while `hasMore` is true. Cursors are opaque and must be sent back unchanged.

        `currentSourceVersion` always contains the complete currently published source, even when that source's chronological entry is outside this page. It and `currentEtag` are read from the same locked configuration snapshot, so clients can use them as an authoritative diff baseline and restore precondition. If `currentEtag` changes between page requests, restart from the first page before presenting a coherent history view.

        This is source history, not a full configuration audit log. A source version retains the transform type and filename recorded with that source, but partner selection, X12 identity, output, business-key, and active-attribute changes are not independently versioned here. `current` compares a stored source hash with the source that is currently published.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl "https://api.modernedi.com/v1/mappings/31/revisions" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY"
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - name: cursor
          in: query
          required: false
          description: Opaque `nextCursor` from the preceding source-history page. Omit for the first page.
          schema:
            type: string
        - name: limit
          in: query
          required: false
          description: Maximum chronological source-version summaries to return. `currentSourceVersion` is returned separately and does not count against this limit.
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
      responses:
        '200':
          description: Source history was returned. The response `ETag` matches `currentEtag` and protects the complete currently published mapping.
          headers:
            ETag:
              $ref: '#/components/headers/ConfigurationEtag'
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MappingSourceVersionListResponse'
        '400':
          $ref: '#/components/responses/InvalidRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          $ref: '#/components/responses/ConfigurationNotFoundError'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
  /v1/mappings/{mappingId}/revisions/{sourceVersionId}:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
      - name: mappingId
        in: path
        required: true
        description: Tenant-scoped mapping id.
        schema:
          type: integer
          minimum: 1
      - name: sourceVersionId
        in: path
        required: true
        description: Source-version id returned by the mapping history endpoint.
        schema:
          type: integer
          minimum: 1
    get:
      operationId: getIntegrationMappingSourceVersion
      tags:
        - Mappings
      summary: Get a historical mapping source
      x-required-scope: configuration:read
      description: |
        **Required key scope:** `configuration:read`.

        Returns one immutable historical transform source for preview or diffing. The source version must belong to the tenant-scoped mapping in the path. Its transform filename is historical context; restoring this version changes only `transform.source` and keeps the currently published filename and all other mapping configuration.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      responses:
        '200':
          description: The source version was returned. The response `ETag` protects the complete currently published mapping, not the immutable historical source.
          headers:
            ETag:
              $ref: '#/components/headers/ConfigurationEtag'
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MappingSourceVersionResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          $ref: '#/components/responses/ConfigurationNotFoundError'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
  /v1/mappings/{mappingId}/revisions/{sourceVersionId}/restore:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
      - name: mappingId
        in: path
        required: true
        description: Tenant-scoped mapping id.
        schema:
          type: integer
          minimum: 1
      - name: sourceVersionId
        in: path
        required: true
        description: Source-version id returned by the mapping history endpoint.
        schema:
          type: integer
          minimum: 1
    post:
      operationId: restoreIntegrationMappingSourceVersion
      tags:
        - Mappings
      summary: Restore a historical mapping source
      x-required-scope: configuration:write
      description: |
        **Required key scope:** `configuration:write`.

        Republishes the selected historical `transform.source` as the current source. Send the mapping's current ETag, available from `GET /v1/mappings/{mappingId}` or the history response's `currentEtag`, in `If-Match`. This prevents a restore from overwriting a concurrent workspace or API edit.

        Restore deliberately keeps the current mapping's transform filename, partner, direction, X12 selection, output, business key, and active attributes. ModernEDI validates the historical source against that current configuration using the same parser or compiler as a normal mapping PATCH. A validation failure returns `422 mapping_validation_failed` without changing the mapping. A successful restore follows the normal runtime-publication lane and returns the newly current mapping and ETag. Because source history is deduplicated by source hash, restoring a saved source makes its existing version current; it does not create a duplicate immutable history entry.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl -X POST "https://api.modernedi.com/v1/mappings/31/revisions/87/restore" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY" \
              -H 'If-Match: "<etag-from-the-latest-mapping-or-history-response>"'
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          description: The historical source was validated and published. The response `ETag` matches `mapping.etag`; inspect `runtimePublication` to determine whether the committed change is already active in the tenant runtime.
          headers:
            ETag:
              $ref: '#/components/headers/ConfigurationEtag'
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MappingMutationResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          $ref: '#/components/responses/ConfigurationNotFoundError'
        '409':
          $ref: '#/components/responses/MappingNotPubliclyManageableError'
        '412':
          $ref: '#/components/responses/EtagMismatchError'
        '413':
          $ref: '#/components/responses/ConfigurationMutationTooLargeError'
        '422':
          $ref: '#/components/responses/MappingValidationError'
        '428':
          $ref: '#/components/responses/PreconditionRequiredError'
  /v1/mapped-outputs:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    get:
      operationId: pollMappedOutputs
      tags:
        - Mapped Output Queue
      summary: Poll mapped inbound output
      x-required-scope: transactions:read
      description: >
        **Required key scope:** `transactions:read`.

        Returns server-managed mapped inbound documents for this tenant and
        temporarily marks them in progress. Transaction-record-only results and
        legacy direct-to-SQS or direct-to-DynamoDB outputs are not duplicated
        into this queue. If a returned message is not acknowledged before the
        visibility timeout expires, ModernEDI may return it again on a later
        poll. Push delivery uses the same queue state: a successful 2xx webhook
        response acknowledges the message, while a failed push leaves it
        available through this endpoint. Each request performs a bounded scan.
        When `hasMore` is true, send `nextCursor` as the next request's `cursor`
        so polling advances past unavailable or output-free transactions instead
        of rescanning them. This endpoint runs on the shared ModernEDI API host,
        not on the tenant AS2 hostname.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl "https://api.modernedi.com/v1/mapped-outputs?limit=10&visibilityTimeoutSeconds=120" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY"
        - lang: Python
          label: Python
          source: |
            import os
            import requests

            response = requests.get(
                "https://api.modernedi.com/v1/mapped-outputs",
                params={"limit": 10, "visibilityTimeoutSeconds": 120},
                headers={"x-api-key": os.environ["MODERNEDI_INTEGRATION_API_KEY"]},
                timeout=30,
            )
            response.raise_for_status()
            messages = response.json()["messages"]
        - lang: JavaScript
          label: Node.js
          source: |
            const url = new URL("https://api.modernedi.com/v1/mapped-outputs")
            url.search = new URLSearchParams({
              limit: "10",
              visibilityTimeoutSeconds: "120",
            })

            const response = await fetch(url, {
              headers: {
                "x-api-key": process.env.MODERNEDI_INTEGRATION_API_KEY,
              },
            })
            if (!response.ok) {
              throw new Error(await response.text())
            }
            const { messages } = await response.json()
      servers:
        - url: https://api.modernedi.com
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/TransactionEnvironment'
        - name: startDate
          in: query
          required: false
          schema:
            type: string
          description: >
            Optional inclusive transaction-window start. Accepts an ISO date or
            an RFC 3339 date-time with an explicit offset. Date-only values start
            at 00:00:00 UTC. Date-times are normalized to UTC; offset-less
            date-times are rejected.
          example: '2026-07-01T00:00:00.000000000Z'
        - name: endDate
          in: query
          required: false
          schema:
            type: string
          description: >
            Optional inclusive transaction-window end. Accepts the same formats
            as `startDate`; a date-only value includes the complete UTC day.
          example: '2026-07-15'
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: >
            Opaque continuation cursor returned as `nextCursor`. When supplied,
            it retains the original transaction window; omit `startDate` and
            `endDate` or send the same normalized values.
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 10
          description: Maximum number of mapped output messages to return.
        - name: visibilityTimeoutSeconds
          in: query
          required: false
          schema:
            type: integer
            minimum: 5
            maximum: 43200
            default: 120
          description: Seconds ModernEDI keeps returned messages hidden from later polls while your system processes them.
        - name: direction
          in: query
          required: false
          schema:
            type: string
            enum:
              - inbound
              - outbound
          description: Transaction direction. Mapped outputs normally come from inbound transactions.
        - name: partnerName
          in: query
          required: false
          schema:
            type: string
          description: Case-insensitive persisted partner name.
        - name: partnerId
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
          description: Tenant-scoped partner id returned by `GET /v1/partners`.
        - name: transactionSet
          in: query
          required: false
          schema:
            type: string
          description: X12 transaction-set identifier such as `850` or `856`.
        - name: businessKey
          in: query
          required: false
          schema:
            type: string
          description: Exact mapped-output business-key value.
        - name: messageId
          in: query
          required: false
          schema:
            type: string
          description: Exact AS2 Message-Id.
        - name: replyToMessageId
          in: query
          required: false
          schema:
            type: string
          description: Exact original inbound Message-Id recorded on a reply.
        - name: transactionControlNumber
          in: query
          required: false
          schema:
            type: string
          description: Exact ST02 transaction control number.
        - name: functionalGroupControlNumber
          in: query
          required: false
          schema:
            type: string
          description: Exact GS06 functional-group control number.
      responses:
        '200':
          description: Mapped output messages were returned for processing.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MappedOutputQueueResponse'
              example:
                success: true
                environment: production
                messages:
                  - id: 7e8276c1d55988a4bf7fbf9f8c2d87187a24bd316dfed84bbf64397b8d5b6e4a
                    receiptHandle: 0af7dd1d-b6a1-4ff9-8ad1-d395de0b97f2
                    deliveryCount: 1
                    leasedUntil: '2026-04-29T15:35:00.000000000Z'
                    environment: production
                    messageId: msg-850-api
                    transactionKey: 17#0001
                    partnerName: Customer One
                    partnerId: 1
                    transactionTimestamp: '2026-04-29T15:33:00.000000000Z'
                    transactionSet:
                      code: '850'
                      description: Purchase Order
                    controlNumbers:
                      transaction: '0001'
                      functionalGroup: '17'
                    edi:
                      x12Version: '4010'
                      functionalIdentifierCode: PO
                      transactionGroupType: '850'
                      segmentTerminator: '~'
                    mapping:
                      id: 44
                      type: ManagedQueueMapping
                      file: customer-one-850.jslt
                      fileSha256Hash: 5vG51TfOLEo9YN0WSMf0bj+xzluq2UptTm4flVSo8EE=
                    mappedOutputKey: 17#0001#00001
                    sequenceNumber: 1
                    purpose: PROCESSING
                    contentType: application/json
                    createdAt: '2026-04-29T15:33:04.000000000Z'
                    businessKey:
                      name: purchaseOrderNumber
                      value: PO-100045
                    extraFields:
                      destination: managedQueue
                    payload: '{"purchaseOrderNumber":"PO-100045","shipTo":{"name":"Main DC"}}'
                limit: 10
                visibilityTimeoutSeconds: 120
                nextCursor: null
                hasMore: false
        '400':
          description: Invalid cursor, date window, limit, or visibility timeout.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformErrorResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
  /v1/mapped-outputs/{id}/ack:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    post:
      operationId: acknowledgeMappedOutput
      tags:
        - Mapped Output Queue
      summary: Mark mapped output as received
      x-required-scope: messages:write
      description: >
        **Required key scope:** `messages:write`.

        Records that your integration acknowledged custody of a previously returned
        mapped output message. Call this after the mapped output has been durably
        stored or enqueued for reliable processing in your system. This acknowledgment
        does not prove later ERP or business processing. The receipt handle must match
        the latest poll response for this mapped output id. A retry with the same
        id and receipt handle is idempotent: it returns the original
        acknowledgment and `ackedAt` value instead of failing. If a call ends
        without a definitive response, retry that same acknowledgment before
        polling. Poll for a new receipt only when the old lease is rejected or
        the message becomes available again; an acknowledged item is no longer
        returned by polling.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl -X POST "https://api.modernedi.com/v1/mapped-outputs/7e8276c1d55988a4bf7fbf9f8c2d87187a24bd316dfed84bbf64397b8d5b6e4a/ack" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY" \
              -H "Content-Type: application/json" \
              --data '{"receiptHandle":"0af7dd1d-b6a1-4ff9-8ad1-d395de0b97f2"}'
        - lang: Python
          label: Python
          source: |
            import os
            import requests

            mapped_output_id = "7e8276c1d55988a4bf7fbf9f8c2d87187a24bd316dfed84bbf64397b8d5b6e4a"
            response = requests.post(
                f"https://api.modernedi.com/v1/mapped-outputs/{mapped_output_id}/ack",
                headers={"x-api-key": os.environ["MODERNEDI_INTEGRATION_API_KEY"]},
                json={"receiptHandle": "0af7dd1d-b6a1-4ff9-8ad1-d395de0b97f2"},
                timeout=30,
            )
            response.raise_for_status()
        - lang: JavaScript
          label: Node.js
          source: |
            const mappedOutputId = "7e8276c1d55988a4bf7fbf9f8c2d87187a24bd316dfed84bbf64397b8d5b6e4a"
            const response = await fetch(`https://api.modernedi.com/v1/mapped-outputs/${mappedOutputId}/ack`, {
              method: "POST",
              headers: {
                "x-api-key": process.env.MODERNEDI_INTEGRATION_API_KEY,
                "Content-Type": "application/json",
              },
              body: JSON.stringify({
                receiptHandle: "0af7dd1d-b6a1-4ff9-8ad1-d395de0b97f2",
              }),
            })
            if (!response.ok) {
              throw new Error(await response.text())
            }
      servers:
        - url: https://api.modernedi.com
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Mapped output id returned by `GET /v1/mapped-outputs`.
        - $ref: '#/components/parameters/TransactionEnvironment'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MappedOutputAckRequest'
            example:
              receiptHandle: 0af7dd1d-b6a1-4ff9-8ad1-d395de0b97f2
      responses:
        '200':
          description: Mapped output was marked as received.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MappedOutputAckResponse'
              example:
                success: true
                environment: production
                acknowledgment:
                  id: 7e8276c1d55988a4bf7fbf9f8c2d87187a24bd316dfed84bbf64397b8d5b6e4a
                  messageId: msg-850-api
                  mappedOutputKey: 17#0001#00001
                  ackedAt: '2026-04-29T15:34:10.000000000Z'
                id: 7e8276c1d55988a4bf7fbf9f8c2d87187a24bd316dfed84bbf64397b8d5b6e4a
                messageId: msg-850-api
                mappedOutputKey: 17#0001#00001
                ackedAt: '2026-04-29T15:34:10.000000000Z'
        '400':
          description: Missing receipt handle or no current in-progress message matched the id and receipt handle.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformErrorResponse'
        '413':
          $ref: '#/components/responses/PayloadTooLargeError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
  /v1/mapped-outputs/ack:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    post:
      operationId: acknowledgeMappedOutputs
      tags:
        - Mapped Output Queue
      summary: Atomically acknowledge multiple mapped outputs
      x-required-scope: messages:write
      description: >
        **Required key scope:** `messages:write`.

        Acknowledges between 1 and 100 mapped-output receipts in one database
        transaction. If any id or receipt handle is invalid, none of the items
        are acknowledged. Repeating an already successful request with the same
        id and receipt handles is idempotent and returns the original
        acknowledgment timestamps. Retry the same batch first when a call ends
        without a definitive response; already acknowledged items are not
        returned by polling. All items must belong to the environment
        selected by the `environment` query parameter.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/TransactionEnvironment'
      requestBody:
        required: true
        description: Mapped-output acknowledgment bodies may be at most 16 KiB (16,384 bytes).
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MappedOutputBulkAckRequest'
      responses:
        '200':
          description: Every receipt was acknowledged or had already been acknowledged.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MappedOutputBulkAckResponse'
        '400':
          description: The batch was empty, exceeded 100 items, or contained an invalid receipt.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          $ref: '#/components/responses/PayloadTooLargeError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
  /v1/integration/transactions:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    get:
      operationId: listIntegrationTransactions
      tags:
        - Transaction Viewer
      summary: List transactions
      x-required-scope: transactions:read
      description: >
        **Required key scope:** `transactions:read`.

        Returns a pageable transaction history for custom transaction viewers,
        dashboards, and reconciliation workflows. Outbound replies include
        `replyToMessageId` so they can be shown underneath the inbound document
        they answer. Each row includes the stable `needsAttention` operator
        signal, machine-readable `attentionReasons`, and a compact
        `mappingStatus` so unattended integrations can route failures without
        fetching every transaction detail. The root `attentionSummary` is an
        environment-wide count and reconciliation-freshness signal; it is not
        restricted to the requested page or date window.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl "https://api.modernedi.com/v1/integration/transactions?startDate=2026-03-01T00:00:00.000000000Z&endDate=2026-03-31T23:59:59.999999999Z&direction=inbound&partnerId=1&transactionSet=850&limit=25" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY"
        - lang: Python
          label: Python
          source: |
            import os
            import requests

            response = requests.get(
                "https://api.modernedi.com/v1/integration/transactions",
                params={
                    "startDate": "2026-03-01T00:00:00.000000000Z",
                    "endDate": "2026-03-31T23:59:59.999999999Z",
                    "direction": "inbound",
                    "partnerId": 1,
                    "transactionSet": "850",
                    "limit": 25,
                },
                headers={"x-api-key": os.environ["MODERNEDI_INTEGRATION_API_KEY"]},
                timeout=30,
            )
            response.raise_for_status()
            transactions = response.json()["transactions"]
        - lang: JavaScript
          label: Node.js
          source: |
            const url = new URL("https://api.modernedi.com/v1/integration/transactions")
            url.search = new URLSearchParams({
              startDate: "2026-03-01T00:00:00.000000000Z",
              endDate: "2026-03-31T23:59:59.999999999Z",
              direction: "inbound",
              partnerId: "1",
              transactionSet: "850",
              limit: "25",
            })

            const response = await fetch(url, {
              headers: {
                "x-api-key": process.env.MODERNEDI_INTEGRATION_API_KEY,
              },
            })
            if (!response.ok) {
              throw new Error(await response.text())
            }
            const { transactions } = await response.json()
      servers:
        - url: https://api.modernedi.com
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/TransactionEnvironment'
        - name: needsAttention
          in: query
          required: false
          schema:
            type: boolean
          description: >
            Filters by the authoritative operator-inbox projection. Use `true`
            to return transactions that currently need attention or `false` to
            return transactions with no active attention reason. Check
            `attentionSummary.freshness.complete` before treating an empty
            filtered page as proof that no transaction needs attention.
        - name: attentionReason
          in: query
          required: false
          style: form
          explode: true
          schema:
            type: array
            items:
              $ref: '#/components/schemas/TransactionAttentionReason'
          description: >
            Repeat this parameter to select more than one attention reason;
            repeated values are ORed. Supplying a reason implies
            `needsAttention=true`, and combining a reason with
            `needsAttention=false` returns `400`. Repeat all explicit filters,
            including every reason, when following an opaque cursor.
        - name: startDate
          in: query
          required: false
          schema:
            type: string
          description: >
            Optional inclusive window start. Accepts an ISO date or an RFC 3339
            date-time with an explicit offset. Date-only values start at
            00:00:00 UTC. Date-times are normalized to UTC; offset-less
            date-times are rejected.
          example: '2026-03-01T00:00:00.000000000Z'
        - name: endDate
          in: query
          required: false
          schema:
            type: string
          description: >
            Optional inclusive window end. Accepts the same formats as
            `startDate`; a date-only value includes the complete UTC day.
          example: '2026-03-31'
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Cursor from the previous response.
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 250
            default: 50
          description: Maximum number of transactions to return.
        - name: direction
          in: query
          required: false
          schema:
            type: string
            enum:
              - inbound
              - outbound
        - name: partnerName
          in: query
          required: false
          schema:
            type: string
          description: Case-insensitive persisted partner name. This filters display metadata; transaction access remains scoped by persisted `partnerId`.
        - name: partnerId
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
          description: >
            Filters transactions by the tenant-scoped partner id returned by
            `GET /v1/partners`. The id remains stable when a partner is renamed.
            Transactions without a stored id are not returned.
          example: 1
        - name: transactionSet
          in: query
          required: false
          schema:
            type: string
          description: X12 transaction set code.
          example: '850'
        - name: businessKey
          in: query
          required: false
          schema:
            type: string
          description: >
            Exact business key value recorded on a transaction. Inbound keys
            come from the incoming map. Outbound keys are recorded only when
            `/send`, `/reply`, `/carbonCopy`, or a `/v1/as2/x12/...` endpoint is
            called with a JSON envelope that includes `businessKey`.
          example: PO-100045
        - name: messageId
          in: query
          required: false
          schema:
            type: string
          description: Exact AS2 message id.
        - name: replyToMessageId
          in: query
          required: false
          schema:
            type: string
          description: Original inbound AS2 message id for outbound replies.
        - name: transactionControlNumber
          in: query
          required: false
          schema:
            type: string
          description: ST02 transaction control number.
        - name: functionalGroupControlNumber
          in: query
          required: false
          schema:
            type: string
          description: GS06 functional group control number.
      responses:
        '200':
          description: Transactions were returned.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionListResponse'
              example:
                success: true
                environment: production
                transactions:
                  - id: msg-850-api/17#0001
                    environment: production
                    needsAttention: true
                    attentionReasons:
                      - watchlist
                    mappingStatus:
                      status: NOT_RECORDED
                      latestAttemptAt: null
                      failureCount: 0
                    messageId: msg-850-api
                    transactionKey: 17#0001
                    replyToMessageId: null
                    direction: inbound
                    partnerName: Customer One
                    partnerId: 1
                    timestamp: '2026-03-25T18:15:00.000000000Z'
                    transactionSet:
                      code: '850'
                      description: Purchase Order
                    controlNumbers:
                      transaction: '0001'
                      functionalGroup: '17'
                    edi:
                      x12Version: '4010'
                      functionalIdentifierCode: PO
                      transactionGroupType: '850'
                      segmentTerminator: '~'
                    businessKey:
                      name: purchaseOrderNumber
                      value: PO-100045
                    functionalAckOutcome:
                      status: accepted
                      acknowledgmentCode: A
                      summary: Functional group accepted; 1 of 1 received transaction set was accepted.
                nextCursor: null
                hasMore: false
                window:
                  startDate: '2026-03-01T00:00:00.000000000Z'
                  endDate: '2026-03-31T23:59:59.999999999Z'
                  timeZone: UTC
                attentionSummary:
                  needsAttentionCount: 0
                  reasonCounts:
                    mappingFailure: 0
                    functionalAckIssue: 0
                    mappedOutputDelivery: 0
                    watchlist: 0
                  freshness:
                    status: pending
                    complete: false
                    lastCompletedAt: null
                    reconciliationStartedAt: null
                    message: Attention indexing has not completed yet; an empty result does not mean there are no issues.
        '400':
          description: Invalid cursor, date window, or limit.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformErrorResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
  /v1/integration/transactions/{messageId}/{transactionKey}:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    get:
      operationId: getIntegrationTransaction
      tags:
        - Transaction Viewer
      summary: Get transaction detail
      x-required-scope: transactions:read
      description: >
        **Required key scope:** `transactions:read`.

        Returns the transaction summary, downloadable document payloads, mapped
        inbound outputs with managed handoff state, timeline events, linked
        outbound reply transactions, and the complete mapping-attempt history
        used to explain `needsAttention`.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl "https://api.modernedi.com/v1/integration/transactions/msg-850-api/17%230001" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY"
        - lang: Python
          label: Python
          source: |
            import os
            from urllib.parse import quote

            import requests

            message_id = "msg-850-api"
            transaction_key = "17#0001"
            message_path = quote(message_id, safe="")
            transaction_path = quote(transaction_key, safe="")
            response = requests.get(
                f"https://api.modernedi.com/v1/integration/transactions/{message_path}/{transaction_path}",
                headers={"x-api-key": os.environ["MODERNEDI_INTEGRATION_API_KEY"]},
                timeout=30,
            )
            response.raise_for_status()
            transaction = response.json()["transaction"]
        - lang: JavaScript
          label: Node.js
          source: |
            const messageId = encodeURIComponent("msg-850-api")
            const transactionKey = encodeURIComponent("17#0001")
            const response = await fetch(
              `https://api.modernedi.com/v1/integration/transactions/${messageId}/${transactionKey}`,
              {
                headers: {
                  "x-api-key": process.env.MODERNEDI_INTEGRATION_API_KEY,
                },
              },
            )
            if (!response.ok) {
              throw new Error(await response.text())
            }
            const { transaction } = await response.json()
      servers:
        - url: https://api.modernedi.com
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/MessageId'
        - $ref: '#/components/parameters/TransactionKey'
        - $ref: '#/components/parameters/TransactionEnvironment'
      responses:
        '200':
          description: Transaction detail was returned.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionDetailResponse'
        '400':
          description: Missing or invalid lookup values.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformErrorResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          description: Transaction was not found or is not visible to this tenant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformErrorResponse'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
  /v1/integration/transactions/{messageId}/{transactionKey}/documents:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    get:
      operationId: getIntegrationTransactionDocuments
      tags:
        - Transaction Viewer
      summary: Get transaction documents
      x-required-scope: transactions:read
      description: >
        **Required key scope:** `transactions:read`.

        Returns raw X12, mapped inbound outputs, MDN reports, acknowledgements,
        and HTTP response bodies recorded for the transaction.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl "https://api.modernedi.com/v1/integration/transactions/msg-850-api/17%230001/documents" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY"
        - lang: Python
          label: Python
          source: |
            import os
            from urllib.parse import quote

            import requests

            message_id = "msg-850-api"
            transaction_key = "17#0001"
            message_path = quote(message_id, safe="")
            transaction_path = quote(transaction_key, safe="")
            response = requests.get(
                f"https://api.modernedi.com/v1/integration/transactions/{message_path}/{transaction_path}/documents",
                headers={"x-api-key": os.environ["MODERNEDI_INTEGRATION_API_KEY"]},
                timeout=30,
            )
            response.raise_for_status()
            documents = response.json()["documents"]
        - lang: JavaScript
          label: Node.js
          source: |
            const messageId = encodeURIComponent("msg-850-api")
            const transactionKey = encodeURIComponent("17#0001")
            const response = await fetch(
              `https://api.modernedi.com/v1/integration/transactions/${messageId}/${transactionKey}/documents`,
              {
                headers: {
                  "x-api-key": process.env.MODERNEDI_INTEGRATION_API_KEY,
                },
              },
            )
            if (!response.ok) {
              throw new Error(await response.text())
            }
            const { documents } = await response.json()
      servers:
        - url: https://api.modernedi.com
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/MessageId'
        - $ref: '#/components/parameters/TransactionKey'
        - $ref: '#/components/parameters/TransactionEnvironment'
      responses:
        '200':
          description: Transaction documents were returned.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionDocumentsResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          description: Transaction was not found or is not visible to this tenant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformErrorResponse'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
  /v1/integration/transactions/{messageId}/{transactionKey}/events:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    get:
      operationId: getIntegrationTransactionEvents
      tags:
        - Transaction Viewer
      summary: Get transaction events
      x-required-scope: transactions:read
      description: >
        **Required key scope:** `transactions:read`.

        Returns a normalized timeline for the transaction, including receipt or
        send events, mapping events, MDNs, acknowledgements, and linked replies.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl "https://api.modernedi.com/v1/integration/transactions/msg-850-api/17%230001/events" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY"
        - lang: Python
          label: Python
          source: |
            import os
            from urllib.parse import quote

            import requests

            message_id = "msg-850-api"
            transaction_key = "17#0001"
            message_path = quote(message_id, safe="")
            transaction_path = quote(transaction_key, safe="")
            response = requests.get(
                f"https://api.modernedi.com/v1/integration/transactions/{message_path}/{transaction_path}/events",
                headers={"x-api-key": os.environ["MODERNEDI_INTEGRATION_API_KEY"]},
                timeout=30,
            )
            response.raise_for_status()
            events = response.json()["events"]
        - lang: JavaScript
          label: Node.js
          source: |
            const messageId = encodeURIComponent("msg-850-api")
            const transactionKey = encodeURIComponent("17#0001")
            const response = await fetch(
              `https://api.modernedi.com/v1/integration/transactions/${messageId}/${transactionKey}/events`,
              {
                headers: {
                  "x-api-key": process.env.MODERNEDI_INTEGRATION_API_KEY,
                },
              },
            )
            if (!response.ok) {
              throw new Error(await response.text())
            }
            const { events } = await response.json()
      servers:
        - url: https://api.modernedi.com
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/MessageId'
        - $ref: '#/components/parameters/TransactionKey'
        - $ref: '#/components/parameters/TransactionEnvironment'
      responses:
        '200':
          description: Transaction events were returned.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionEventsResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          description: Transaction was not found or is not visible to this tenant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformErrorResponse'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
  /v1/integration/transactions/{messageId}/{transactionKey}/related:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    get:
      operationId: getRelatedIntegrationTransactions
      tags:
        - Transaction Viewer
      summary: Get linked reply transactions
      x-required-scope: transactions:read
      description: >
        **Required key scope:** `transactions:read`.

        Returns outbound reply transactions linked to an inbound transaction by
        `replyToMessageId`. Use this to render replies underneath the inbound
        document in a custom viewer.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl "https://api.modernedi.com/v1/integration/transactions/msg-850-api/17%230001/related" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY"
        - lang: Python
          label: Python
          source: |
            import os
            from urllib.parse import quote

            import requests

            message_id = "msg-850-api"
            transaction_key = "17#0001"
            message_path = quote(message_id, safe="")
            transaction_path = quote(transaction_key, safe="")
            response = requests.get(
                f"https://api.modernedi.com/v1/integration/transactions/{message_path}/{transaction_path}/related",
                headers={"x-api-key": os.environ["MODERNEDI_INTEGRATION_API_KEY"]},
                timeout=30,
            )
            response.raise_for_status()
            related_transactions = response.json()["relatedTransactions"]
        - lang: JavaScript
          label: Node.js
          source: |
            const messageId = encodeURIComponent("msg-850-api")
            const transactionKey = encodeURIComponent("17#0001")
            const response = await fetch(
              `https://api.modernedi.com/v1/integration/transactions/${messageId}/${transactionKey}/related`,
              {
                headers: {
                  "x-api-key": process.env.MODERNEDI_INTEGRATION_API_KEY,
                },
              },
            )
            if (!response.ok) {
              throw new Error(await response.text())
            }
            const { relatedTransactions } = await response.json()
      servers:
        - url: https://api.modernedi.com
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/MessageId'
        - $ref: '#/components/parameters/TransactionKey'
        - $ref: '#/components/parameters/TransactionEnvironment'
      responses:
        '200':
          description: Related transactions were returned.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RelatedTransactionsResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          description: Transaction was not found or is not visible to this tenant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformErrorResponse'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
  /v1/integration/transactions/{messageId}/{transactionKey}/replays:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
      - $ref: '#/components/parameters/MessageId'
      - $ref: '#/components/parameters/TransactionKey'
    post:
      operationId: replayIntegrationTransaction
      tags:
        - Transaction Viewer
      summary: Replay an inbound transaction without redelivery
      x-required-scope: transactions:replay
      description: |
        **Required key scope:** `transactions:replay`.

        Re-runs mapping for one tenant-visible inbound transaction with the currently published maps and returns the regenerated mapped outputs directly. Historical map revision replay is not part of the public API.

        The only supported delivery is `response_only`. ModernEDI does not put regenerated outputs on the mapped-output queue, invoke mapped-output webhooks, or generate outbound acknowledgements. This endpoint is therefore suitable for inspection and controlled recovery without repeating downstream delivery side effects.

        `Idempotency-Key` is required. Repeating the same key and request returns the stored successful replay with `Idempotency-Replayed: true` and does not invoke mapping again. Reusing the key for different replay input returns `409 idempotency_key_conflict`; retry while the same replay is still processing returns `409 idempotency_key_in_progress` with `retryable: true`.

        The replay body is strict JSON: unknown fields are rejected, and `environment`, `mode`, `delivery`, and `reason` must be strings when present (`reason` may also be `null`). Type mismatches return `400 invalid_request` with the offending JSON pointer.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl -X POST \
              "https://api.modernedi.com/v1/integration/transactions/msg-850-api/17%230001/replays" \
              -H "Content-Type: application/json" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY" \
              -H "Idempotency-Key: replay-msg-850-api-17-0001-v1" \
              --data @examples/transaction-replay-request.json
        - lang: Python
          label: Python
          source: |
            import os
            import requests

            response = requests.post(
                "https://api.modernedi.com/v1/integration/transactions/msg-850-api/17%230001/replays",
                headers={
                    "x-api-key": os.environ["MODERNEDI_INTEGRATION_API_KEY"],
                    "Idempotency-Key": "replay-msg-850-api-17-0001-v1",
                },
                json={
                    "environment": "production",
                    "mode": "current_maps",
                    "delivery": "response_only",
                    "reason": "Verify corrected purchase-order mapping",
                },
                timeout=60,
            )
            response.raise_for_status()
            regenerated_outputs = response.json()["regeneratedOutputs"]
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - $ref: '#/components/parameters/ReplayIdempotencyKey'
      requestBody:
        required: true
        description: Replay request bodies may be at most 16 KiB (16,384 bytes).
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TransactionReplayRequest'
      responses:
        '200':
          description: Mapping replay completed or a stored successful result was returned.
          headers:
            Idempotency-Replayed:
              $ref: '#/components/headers/ReplayIdempotencyReplayed'
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionReplayResponse'
        '400':
          $ref: '#/components/responses/InvalidRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          description: The selected inbound transaction was not found in this tenant and environment.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: The idempotency key conflicts with another request or the same replay is still processing.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          $ref: '#/components/responses/PayloadTooLargeError'
        '422':
          description: The selected transaction is outbound or otherwise cannot be replayed with the selected map mode.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Mapping replay failed. Inspect `error.retryable` before retrying the same idempotency key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/partners/{partnerId}/mapped-output-webhook:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    get:
      operationId: getMappedOutputWebhookStatus
      tags:
        - Mapped Output Queue
      summary: Get mapped-output webhook status
      x-required-scope: configuration:read
      description: >
        **Required key scope:** `configuration:read`.

        Returns the current mapped-output webhook configuration and recent
        delivery health for a partner. This read-only endpoint never returns the
        signing secret; it reports whether a secret is configured and only its
        last four characters so operators can identify the active credential.
        Save the response `ETag` header or `webhook.etag` before updating.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - name: partnerId
          in: path
          required: true
          description: Tenant-scoped partner id returned by `GET /v1/partners`.
          schema:
            type: integer
      responses:
        '200':
          description: Webhook status was returned.
          headers:
            ETag:
              $ref: '#/components/headers/ConfigurationEtag'
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MappedOutputWebhookStatusResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          description: The partner was not found in this tenant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
    put:
      operationId: updateMappedOutputWebhook
      tags:
        - Mapped Output Queue
      summary: Configure mapped-output webhook delivery
      x-required-scope: configuration:write
      description: |
        **Required key scope:** `configuration:write`.

        Enables, disables, or rotates a partner's HTTPS mapped-output webhook. Send the current configuration ETag in `If-Match`. `endpointUrl` is required when enabling delivery and must be an absolute HTTPS URL without user information or a fragment and use a publicly routable host. Localhost, private, link-local, multicast, carrier-grade NAT, documentation, benchmark, and other reserved address targets are rejected. ModernEDI resolves the hostname again before every webhook delivery and sends only when all resolved addresses are publicly routable. Enabling a webhook without an existing signing secret creates one; `rotateSigningSecret: true` replaces it.

        The request body is strict JSON: unknown fields are rejected; `enabled` and `rotateSigningSecret` must be booleans; and `endpointUrl` must be a string or `null`. Type mismatches return `400 invalid_request` with the offending JSON pointer.

        A newly created or rotated `plainTextSigningSecret` appears only in this successful response. Save it immediately. Later reads expose only `signingSecretConfigured` and `signingSecretLastFour`.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl -X PUT "https://api.modernedi.com/v1/partners/1/mapped-output-webhook" \
              -H "Content-Type: application/json" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY" \
              -H 'If-Match: "<etag-from-the-latest-webhook-response>"' \
              --data @examples/mapped-output-webhook-update-request.json
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - name: partnerId
          in: path
          required: true
          description: Tenant-scoped partner id returned by `GET /v1/partners`.
          schema:
            type: integer
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        description: Partner webhook configuration bodies may be at most 1 MiB (1,048,576 bytes).
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MappedOutputWebhookUpdateRequest'
      responses:
        '200':
          description: Webhook configuration was saved. A new signing secret is included only when created or rotated.
          headers:
            ETag:
              $ref: '#/components/headers/ConfigurationEtag'
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MappedOutputWebhookUpdateResponse'
        '400':
          $ref: '#/components/responses/InvalidRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '404':
          description: The partner was not found in this tenant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '412':
          $ref: '#/components/responses/EtagMismatchError'
        '413':
          $ref: '#/components/responses/PayloadTooLargeError'
        '428':
          $ref: '#/components/responses/PreconditionRequiredError'
  /v1/usage:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    get:
      operationId: getIntegrationUsage
      tags:
        - Account
      summary: Get plan usage and enforcement status
      x-required-scope: transactions:read
      description: >
        **Required key scope:** `transactions:read`.

        Returns the tenant's current AS2 message usage, plan thresholds, daily
        history, and operational signals. Use `rejectionActive`, `status`, and
        the threshold fields to warn operators before additional messages are
        rejected. The quota date and `timeZone` are reported in UTC and usage
        includes both inbound and outbound AS2 messages. Hour bucket labels use
        `HH:00` values such as `18:00`.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - name: days
          in: query
          required: false
          description: Number of UTC calendar days to include.
          schema:
            type: integer
            minimum: 1
            maximum: 90
            default: 14
      responses:
        '200':
          description: Usage was returned.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UsageResponse'
        '400':
          description: The requested day count was outside the supported range.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
  /v1/x12/validate:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    post:
      operationId: validateX12
      tags:
        - X12 Tools
      summary: Parse and validate an X12 interchange
      x-required-scope: messages:write
      description: >
        **Required key scope:** `messages:write`.

        Parses an X12 interchange and returns structural, group, transaction,
        segment, and element validation results without sending anything. Send
        X12 directly as `application/edi-x12` or `text/plain`, or wrap it in an
        `x12` field when JSON is more convenient. A syntactically invalid X12
        document still returns a 200 validation result; malformed request shapes
        return a standard 400 error. Request bodies are limited to 3 MiB.
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl -X POST "https://api.modernedi.com/v1/x12/validate" \
              -H "x-api-key: $MODERNEDI_INTEGRATION_API_KEY" \
              -H "Content-Type: application/edi-x12" \
              --data-binary @purchase-order.edi
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      requestBody:
        required: true
        content:
          application/edi-x12:
            schema:
              type: string
          text/plain:
            schema:
              type: string
          application/json:
            schema:
              $ref: '#/components/schemas/X12ValidationRequest'
      responses:
        '200':
          description: X12 validation completed.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/X12ValidationResponse'
        '400':
          description: The request body was empty or did not use a supported shape.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '413':
          $ref: '#/components/responses/PayloadTooLargeError'
  /v1/x12/versions:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    get:
      operationId: listX12Versions
      tags:
        - X12 Tools
      summary: List available X12 versions
      x-required-scope: configuration:read
      description: |
        **Required key scope:** `configuration:read`.

        Returns X12 versions for which the tenant has reference syntax trees.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      responses:
        '200':
          description: X12 versions were returned.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/X12VersionsResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
  /v1/x12/versions/{x12Version}/transaction-sets:
    parameters:
      - $ref: '#/components/parameters/CallerRequestId'
    get:
      operationId: listX12TransactionSets
      tags:
        - X12 Tools
      summary: List transaction sets for an X12 version
      x-required-scope: configuration:read
      description: >
        **Required key scope:** `configuration:read`.

        Returns the transaction-set identifiers and descriptions available for
        the normalized X12 version. Both `4010` and `004010` style version
        values are accepted.
      security:
        - IntegrationApiKeyHeader: []
        - IntegrationApiKeyBearer: []
      parameters:
        - name: x12Version
          in: path
          required: true
          schema:
            type: string
          example: '004010'
      responses:
        '200':
          description: Transaction sets were returned.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/X12TransactionSetsResponse'
        '400':
          description: The X12 version was invalid.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '402':
          $ref: '#/components/responses/BillingInactiveError'
        '403':
          $ref: '#/components/responses/InsufficientScopeError'
        '414':
          $ref: '#/components/responses/QueryTooLargeError'
components:
  responses:
    UnauthorizedError:
      description: Missing or invalid integration API key.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: unauthorized
              message: Missing or invalid integration API key.
              retryable: false
              requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
              details: {}
    InsufficientScopeError:
      description: The API key is valid but does not grant the operation's required scope.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: insufficient_scope
              message: The Integration API key does not grant the required scope.
              retryable: false
              requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
              details:
                requiredScopes:
                  - configuration:write
                grantedScopes:
                  - configuration:read
    InvalidRequestError:
      description: The request body, query parameters, or configuration values were invalid. Configuration bodies reject unknown fields and wrong JSON scalar types rather than coercing or silently ignoring them.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: invalid_request
              message: Request validation failed.
              retryable: false
              requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
              details: {}
    ConfigurationNotFoundError:
      description: The tenant-scoped configuration object was not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    ConfigurationInUseError:
      description: The configuration object is still referenced and cannot be deleted.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: configuration_in_use
              message: Configuration is still referenced by another object.
              retryable: false
              requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
              details: {}
    ConfigurationConflictError:
      description: A tenant-scoped uniqueness rule was violated while creating or updating configuration.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: configuration_conflict
              message: Another AS2 connection already uses the production AS2 identifier "RETAILER_PROD".
              retryable: false
              requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
              details: {}
    PlanEntitlementRequiredError:
      description: The requested AS2 endpoint transport is not included in the tenant's current plan. The configuration mutation is not committed.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: plan_entitlement_required
              message: Plain HTTP AS2 endpoint URLs are not available on plan "endpoint". Use HTTPS, or upgrade to Endpoint Plus or a higher plan.
              retryable: false
              requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
              details:
                planCode: endpoint
                requiredCapability: plain_http_as2_endpoints
                minimumPlanCode: endpoint_plus
                fields:
                  - /environments/production/endpointUrl
    PartnerX12IdentityValidationError:
      description: A connected partner's final merged X12 identity configuration is incomplete or has unsafe field lengths. The mutation is not committed. Detached partner drafts may remain incomplete until an AS2 connection is attached.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: partner_x12_identity_invalid
              message: Connected partner X12 identities must use a 2-character ISA qualifier and 1-15 character ISA and GS identifiers in both environments.
              retryable: false
              requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
              details:
                violations:
                  - field: /x12/production/isaQualifier
                    minimumLength: 2
                    maximumLength: 2
                    actualLength: 1
                  - field: /x12/test/isaId
                    minimumLength: 1
                    maximumLength: 15
                    actualLength: 0
    MappingNotPubliclyManageableError:
      description: The mapping is visible for inventory and migration but its legacy delivery type cannot be updated through the public API.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: mapping_not_publicly_manageable
              message: Mapping type "SqsMapping" is read-only through the public Integration API.
              retryable: false
              requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
              details:
                mappingId: 44
                mappingType: SqsMapping
    MappingIdempotencyConflictError:
      description: The tenant-scoped idempotency key belongs to a different mapping-create request, or its original mapping was later deleted. The request does not create another mapping.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            changedRequest:
              summary: The key was reused with a different JSON request.
              value:
                success: false
                error:
                  code: idempotency_key_conflict
                  message: Idempotency-Key was already used for a different mapping creation request.
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details:
                    mappingId: 31
            deletedResult:
              summary: The mapping originally created with the key was deleted.
              value:
                success: false
                error:
                  code: idempotency_result_unavailable
                  message: The mapping created with this Idempotency-Key no longer exists. Use a new key to create a new mapping.
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details:
                    mappingId: 31
    MappingValidationError:
      description: The executable mapping configuration did not parse, compile, or pass semantic validation. No mapping revision is published when this response is returned.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: mapping_validation_failed
              message: transform.source is not a valid X12 Mapper program.
              retryable: false
              requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
              details:
                field: transform.source
                diagnostics:
                  - severity: ERROR
                    code: E1001
                    message: The element reference is not valid for the selected X12 transaction set.
                    line: 3
                    column: 12
    EtagMismatchError:
      description: The supplied `If-Match` value is stale. Fetch the resource again and reconcile before retrying.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: etag_mismatch
              message: Configuration changed after it was read. Fetch it again and retry with the new ETag.
              retryable: false
              requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
              details:
                currentEtag: '"new-resource-sha256"'
    PreconditionRequiredError:
      description: >
        `If-Match` is required for configuration updates and deletes.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: precondition_required
              message: If-Match is required for configuration updates and deletes.
              retryable: false
              requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
              details:
                currentEtag: '"resource-sha256"'
    BillingInactiveError:
      description: Tenant billing is not active for protected integration features.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: billing_inactive
              message: Tenant billing status "canceled" does not allow integration API access.
              retryable: false
              requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
              details: {}
    PayloadTooLargeError:
      description: Request body exceeded the endpoint-specific byte limit. Inspect `error.details.maximumBytes`; documented limits include 3 MiB for outbound/validation, 1 MiB for configuration writes, and 16 KiB for replay and mapped-output acknowledgments.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            configurationLimit:
              summary: Configuration request exceeded 1 MiB
              value:
                success: false
                error:
                  code: payload_too_large
                  message: Mapping configuration request body must be 1 MB or smaller.
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details:
                    maximumBytes: 1048576
            replayLimit:
              summary: Replay request exceeded 16 KiB
              value:
                success: false
                error:
                  code: payload_too_large
                  message: Transaction replay request body must be 16 KB or smaller.
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details:
                    maximumBytes: 16384
            outboundLimit:
              summary: Outbound or validation request exceeded 3 MiB
              value:
                success: false
                error:
                  code: payload_too_large
                  message: Outbound AS2 request body must be 3 MB or smaller.
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details:
                    maximumBytes: 3145728
    ConfigurationMutationTooLargeError:
      description: |
        The request exceeded the 1 MiB HTTP body ceiling, or the resulting aggregate tenant runtime configuration would exceed 12 MiB (12,582,912 bytes).

        `payload_too_large` means the submitted request body itself is too large. `runtime_configuration_too_large` means the proposed partner, AS2 connection, or mapping create/update would make the complete runtime configuration document too large; the database mutation is rejected and the existing configuration remains active. Delete remains available above the aggregate cap so configuration can be reduced.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            requestBodyLimit:
              summary: Configuration request exceeded 1 MiB
              value:
                success: false
                error:
                  code: payload_too_large
                  message: Mapping configuration request body must be 1 MB or smaller.
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details:
                    maximumBytes: 1048576
            aggregateRuntimeLimit:
              summary: Resulting runtime configuration exceeded 12 MiB
              value:
                success: false
                error:
                  code: runtime_configuration_too_large
                  message: The resulting tenant runtime configuration would be 12584021 bytes; the maximum is 12582912 bytes.
                  retryable: false
                  requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
                  details:
                    actualBytes: 12584021
                    maximumBytes: 12582912
    QueryTooLargeError:
      description: Query string exceeded 4 KiB.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: query_too_large
              message: Query string exceeded the maximum allowed size.
              retryable: false
              requestId: req_01HZ9TRK7B0YQW2V4RMNC9B60A
              details:
                maximumBytes: 4096
    TenantAs2RateLimitError:
      description: The tenant AS2 runtime asked the client to retry later.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
        Idempotency-Replayed:
          $ref: '#/components/headers/IdempotencyReplayed'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  headers:
    XRequestId:
      description: Correlation id for this request. Log it when contacting ModernEDI support.
      schema:
        type: string
      example: req_01HZ9TRK7B0YQW2V4RMNC9B60A
    RetryAfter:
      description: Downstream retry guidance forwarded from the tenant AS2 runtime.
      schema:
        type: string
      example: '60'
    IdempotencyReplayed:
      description: >
        The value is `true` when the tenant AS2 runtime returned a stored idempotent result.
      schema:
        type: string
        enum:
          - 'true'
    ReplayIdempotencyReplayed:
      description: >
        `true` when this response was loaded from a previous successful replay with the same key and request.
      schema:
        type: string
        enum:
          - 'true'
          - 'false'
    MappingIdempotencyReplayed:
      description: >
        `true` when this response returned the mapping previously created with
        the same tenant-scoped idempotency key and JSON request; `false` when
        this request created the mapping.
      schema:
        type: string
        enum:
          - 'true'
          - 'false'
    ConfigurationEtag:
      description: Quoted SHA-256 ETag for optimistic configuration concurrency. Send this exact value in `If-Match`.
      schema:
        type: string
      example: '"cca9acc426eb9eae4e9d34160c07ce49a50e0dedd0142908d8be0e8703e28a50"'
  requestBodies:
    OutboundMappedSourceBody:
      required: true
      description: >
        Source document for an outgoing map. Use the vendor media type for an
        explicit ModernEDI envelope containing `input`, `contentType`, optional
        `params`, and optional `businessKey`. For backward compatibility,
        `application/json` bodies containing `input` plus envelope metadata are
        also recognized. Other JSON objects, including business documents with
        a top-level field named `contentType`, remain ordinary JSON map input.
      content:
        application/vnd.modernedi.outbound+json:
          schema:
            $ref: '#/components/schemas/OutboundTransformEnvelope'
        application/json:
          schema:
            anyOf:
              - type: object
                additionalProperties: true
              - $ref: '#/components/schemas/OutboundTransformEnvelope'
              - type: array
                items: {}
              - type: string
                nullable: true
              - type: number
              - type: boolean
        application/xml:
          schema:
            type: string
        text/plain:
          schema:
            type: string
    GeneratedX12Body:
      required: true
      description: >
        A complete X12 interchange that ModernEDI should validate and send
        without applying an outgoing map. Use the JSON form only when you also
        want to attach a business key to the transaction record.
      content:
        application/edi-x12:
          schema:
            type: string
        text/plain:
          schema:
            type: string
        application/json:
          schema:
            $ref: '#/components/schemas/GeneratedX12Request'
  parameters:
    CallerRequestId:
      name: X-Request-Id
      in: header
      required: false
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: '^[A-Za-z0-9][A-Za-z0-9._:@/+=-]{0,127}$'
      description: >
        Optional caller correlation id. ModernEDI preserves a value that matches
        this safe format and returns it in the response `X-Request-Id` header;
        otherwise ModernEDI generates a replacement. Always log the response
        value because it is the authoritative id for support correlation.
    TestMode:
      name: test
      in: query
      required: false
      schema:
        type: boolean
        default: false
      description: >
        When `true`, use the test identifiers and test endpoint configured for
        the partner connection. On `/v1/as2/reply` and
        `/v1/as2/x12/reply`, this also selects test transaction history when
        `originalMessageId` is used to infer partner metadata and link the reply.
    TransactionEnvironment:
      name: environment
      in: query
      required: false
      schema:
        type: string
        enum:
          - production
          - test
        default: production
      description: >
        Selects the isolated production or test transaction history and queue.
        Cursors and mapped-output receipt handles are bound to the environment
        that created them and cannot be reused across environments.
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      schema:
        type: string
        minLength: 1
        maxLength: 128
      description: >
        Optional caller-generated key for safely retrying outbound requests.
        Reusing the same key with the same request replays a stored terminal
        result; reusing it with a different request returns an idempotency
        conflict. Retryable downstream `408`, `425`, `429`, and `5xx` results
        are not terminal and a retry can execute the AS2 send again. A replayed
        stored result includes `Idempotency-Replayed: true`. If the tenant AS2
        runtime asks the client to wait, ModernEDI preserves its `Retry-After`
        response header. For the mapped endpoints, mapping selection,
        transformation, and generated-X12 validation happen before the tenant
        AS2 runtime reserves this key. If one of those steps fails and you
        correct the partner or map
        configuration, retry the identical operation and request with the same
        key even when that configuration error reported `retryable: false`.
        Do not change the operation, selectors, source input, content type,
        params, or business key while reusing it. When delivery may have
        succeeded but the terminal
        response was lost, reconcile transaction history and business keys
        before retrying because idempotency cannot rule out a duplicate send.
    ReplayIdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema:
        type: string
        minLength: 1
        maxLength: 200
      description: |
        Required caller-generated replay key. Reuse the same key only for the same message id, transaction key, environment, mode, delivery, and reason. A successful result is stored for safe retry.
    MappingCreateIdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema:
        type: string
        minLength: 1
        maxLength: 200
      description: |
        Required caller-generated key for safely retrying mapping creation. The key is scoped to the tenant, not to one API credential. Reuse it only with the same JSON request. Store one stable key with the intended configuration deployment operation rather than generating a new key for each retry.
    IfMatch:
      name: If-Match
      in: header
      required: true
      schema:
        type: string
      description: Exact quoted ETag returned by the latest GET, POST, or PATCH response for this resource.
    OutboundCustomer:
      name: customer
      in: query
      required: false
      schema:
        type: string
      description: Exact configured partner name used to select the outgoing map. Required unless `partnerId` is supplied. For `/send` and `/reply`, this is also the AS2 destination partner. For `/carbonCopy`, this is the original/source partner used for map selection, not the copy recipient.
      example: Customer One
    ReplyOutboundCustomer:
      name: customer
      in: query
      required: false
      schema:
        type: string
      description: Exact configured partner name used to select the outgoing reply map. Optional for `/reply` when `originalMessageId` identifies an inbound transaction with an unambiguous persisted partner id. An explicit name overrides inferred partner metadata.
      example: Customer One
    OutboundPartnerId:
      name: partnerId
      in: query
      required: false
      schema:
        type: integer
      description: Stable partner id returned by `GET /v1/partners`. Required unless `customer` is supplied, except that `/reply` can infer a persisted partner id from `originalMessageId`. Transactions without a persisted id do not supply inferred partner metadata. When supplied explicitly, ModernEDI uses this id instead of partner-name matching or inferred partner metadata for outgoing-map selection. For `/carbonCopy`, this id selects the original/source partner, not the `copyToCustomer` or `copyToPartnerId` recipient.
      example: 1
    OutboundX12Version:
      name: x12Version
      in: query
      required: true
      schema:
        type: string
      description: X12 version tied to the outgoing map.
      example: '4010'
    ReplyOutboundX12Version:
      name: x12Version
      in: query
      required: false
      schema:
        type: string
      description: X12 version tied to the outgoing reply map. Optional for `/reply` when `originalMessageId` identifies an inbound transaction whose X12 version can be inferred unambiguously. Send this explicitly when the reply must use a different X12 version than the original inbound document.
      example: '4010'
    OutboundFunctionalGroupType:
      name: functionalGroupType
      in: query
      required: true
      schema:
        type: string
      description: X12 functional group code tied to the outgoing map.
      example: IN
    ReplyOutboundFunctionalGroupType:
      name: functionalGroupType
      in: query
      required: false
      schema:
        type: string
      description: X12 functional group code tied to the outgoing reply map. Optional for `/reply` when `originalMessageId`, the resolved partner and X12 version, and request `Content-Type` match exactly one outgoing reply map. Send this explicitly when multiple reply maps could match.
      example: IN
    OutboundTransactionGroupType:
      name: transactionGroupType
      in: query
      required: true
      schema:
        type: integer
      description: X12 transaction set identifier code tied to the outgoing map.
      example: 810
    ReplyOutboundTransactionGroupType:
      name: transactionGroupType
      in: query
      required: false
      schema:
        type: integer
      description: X12 transaction set identifier code tied to the outgoing reply map. Optional for `/reply` when `originalMessageId`, the resolved partner and X12 version, and request `Content-Type` match exactly one outgoing reply map. Send this explicitly when multiple reply maps could match.
      example: 810
    OriginalMessageId:
      name: originalMessageId
      in: query
      required: true
      schema:
        type: string
      description: Original inbound AS2 message id used to link replies or carbon copies to an inbound flow.
      example: msg-850-api
    CopyToCustomer:
      name: copyToCustomer
      in: query
      required: false
      schema:
        type: string
      description: Exact configured partner name that receives the carbon-copy AS2 envelope. Required unless `copyToPartnerId` is supplied. This does not select the outgoing map; `customer` or `partnerId` selects the map.
      example: Broker One
    CopyToPartnerId:
      name: copyToPartnerId
      in: query
      required: false
      schema:
        type: integer
      description: Stable partner id returned by `GET /v1/partners` for the partner that receives the carbon-copy AS2 envelope. Required unless `copyToCustomer` is supplied. This does not select the outgoing map; `customer` or `partnerId` selects the map.
      example: 2
    MessageId:
      name: messageId
      in: path
      required: true
      schema:
        type: string
      description: AS2 message id.
    TransactionKey:
      name: transactionKey
      in: path
      required: true
      schema:
        type: string
      description: Transaction key, usually `<functional-group-control-number>#<transaction-control-number>`.
  securitySchemes:
    IntegrationApiKeyHeader:
      type: apiKey
      in: header
      name: x-api-key
      description: |
        Recommended tenant integration API key header. Keys may grant `transactions:read`, `transactions:replay`, `messages:write`, `configuration:read`, and `configuration:write`. See each operation's required scope.
    IntegrationApiKeyBearer:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: Alternative bearer-token form of the same scoped tenant integration API key.
  schemas:
    PartnerListResponse:
      type: object
      required:
        - success
        - partners
      properties:
        success:
          type: boolean
          enum:
            - true
        partners:
          type: array
          description: Active configured trading partners for this tenant, ordered by partner id. Retired partners are excluded.
          items:
            $ref: '#/components/schemas/PartnerConfiguration'
    IntegrationPartner:
      type: object
      required:
        - partnerId
        - name
        - as2ConnectionConfigured
        - productionReady
        - testReady
        - carbonCopyOnly
        - as2ConnectionId
        - ediSupportEmail
        - unmappedInboundPolicy
        - signingCertificateSlot
        - supportsNewlineAfterSegmentTerminator
        - x12
      properties:
        partnerId:
          type: integer
          description: Stable tenant-scoped partner id. Use this value as the `partnerId` query parameter for exact outbound map selection.
          example: 1
        name:
          type: string
          description: Configured partner name. Use this value as `customer` or `copyToCustomer` when selecting by name.
          example: Customer One
        as2ConnectionId:
          type: integer
          nullable: true
          description: Tenant-scoped partner AS2 connection id, or `null` while connection details are not yet available.
          example: 12
        as2ConnectionConfigured:
          type: boolean
          description: True when this partner has an AS2 connection configured for delivery. Outgoing AS2 calls require the matched destination partner to be configured before ModernEDI can send live traffic.
        productionReady:
          type: boolean
          description: True when the partner has a production AS2 destination endpoint configured.
        testReady:
          type: boolean
          description: True when the partner has a test AS2 destination endpoint configured.
        carbonCopyOnly:
          type: boolean
          description: True when the partner is intended only as a carbon-copy recipient in the workspace.
        ediSupportEmail:
          type: string
          description: Partner EDI support address, or an empty string when one has not been recorded.
          example: edi@customer-one.example
        unmappedInboundPolicy:
          type: string
          enum:
            - MAPPED_ONLY
            - PASSTHROUGH_RAW_X12
          description: Whether inbound transactions without a matching map are rejected from mapped-output delivery or persisted as raw X12.
        signingCertificateSlot:
          type: string
          enum:
            - active
            - next
          description: Tenant certificate slot ModernEDI uses to sign AS2 traffic for this partner.
        supportsNewlineAfterSegmentTerminator:
          type: boolean
          description: Whether ModernEDI may emit a newline after each X12 segment terminator for this partner.
        x12:
          description: Connected partners satisfy `PartnerX12Identities`; detached drafts may contain incomplete authoring values until an AS2 connection is attached.
          anyOf:
            - $ref: '#/components/schemas/PartnerX12Identities'
            - $ref: '#/components/schemas/PartnerDraftX12Identities'
        etag:
          type: string
          description: Quoted SHA-256 ETag over mutable partner configuration, present on list and configuration detail responses. Derived `as2ConnectionConfigured`, `productionReady`, and `testReady` values are excluded; changes to the referenced AS2 connection are guarded by that connection's own ETag.
    PartnerX12Identities:
      type: object
      required:
        - production
        - test
      properties:
        production:
          $ref: '#/components/schemas/PartnerX12Identity'
        test:
          $ref: '#/components/schemas/PartnerX12Identity'
    PartnerX12Identity:
      type: object
      required:
        - isaQualifier
        - isaId
        - gsId
      properties:
        isaQualifier:
          type: string
          minLength: 2
          maxLength: 2
          description: ISA qualifier ModernEDI expects for this partner in the selected environment. Connected partners always use exactly 2 characters; detached draft values may be incomplete.
          example: ZZ
        isaId:
          type: string
          minLength: 1
          maxLength: 15
          description: ISA id ModernEDI expects for this partner in the selected environment. Connected partners always use between 1 and 15 characters inclusive; detached draft values may be incomplete.
          example: CUSTOMERONE
        gsId:
          type: string
          minLength: 1
          maxLength: 15
          description: GS sender or receiver id configured for this partner in the selected environment. Connected partners always use between 1 and 15 characters inclusive; detached draft values may be incomplete.
          example: CUSTOMERONE
    PartnerDraftX12Identities:
      type: object
      description: Resolved authoring values returned for a detached partner draft. Fields are present in responses but may remain incomplete until an AS2 connection is attached.
      required:
        - production
        - test
      properties:
        production:
          $ref: '#/components/schemas/PartnerDraftX12Identity'
        test:
          $ref: '#/components/schemas/PartnerDraftX12Identity'
    PartnerDraftX12Identity:
      type: object
      required:
        - isaQualifier
        - isaId
        - gsId
      properties:
        isaQualifier:
          type: string
          description: Draft ISA qualifier. Attaching an AS2 connection requires exactly 2 characters.
        isaId:
          type: string
          description: Draft ISA id. Attaching an AS2 connection requires between 1 and 15 characters inclusive.
        gsId:
          type: string
          description: Draft GS id. Attaching an AS2 connection requires between 1 and 15 characters inclusive.
    PartnerX12IdentityInputs:
      type: object
      description: Production and test X12 identities supplied on create or patch. Detached drafts may omit either environment or individual identity fields. On create, omitted ISA qualifiers default to `ZZ`, omitted ISA ids default to an empty draft value, and an omitted GS id defaults to the resolved ISA id. On patch, all omitted fields retain their stored values. If the final merged partner is connected, the complete values must satisfy the connected-partner lengths.
      properties:
        production:
          $ref: '#/components/schemas/PartnerX12IdentityInput'
        test:
          $ref: '#/components/schemas/PartnerX12IdentityInput'
    PartnerX12IdentityInput:
      type: object
      properties:
        isaQualifier:
          type: string
          description: Must contain exactly 2 characters whenever the final partner configuration is connected.
        isaId:
          type: string
          description: Must contain between 1 and 15 characters inclusive whenever the final partner configuration is connected.
        gsId:
          type: string
          description: Must contain between 1 and 15 characters inclusive whenever the final partner configuration is connected.
    PartnerConfiguration:
      allOf:
        - $ref: '#/components/schemas/IntegrationPartner'
        - type: object
          required:
            - etag
    PartnerResponse:
      type: object
      required:
        - success
        - partner
      properties:
        success:
          type: boolean
          enum:
            - true
        partner:
          $ref: '#/components/schemas/PartnerConfiguration'
    PartnerMutationResponse:
      allOf:
        - $ref: '#/components/schemas/PartnerResponse'
        - type: object
          required:
            - runtimePublication
          properties:
            runtimePublication:
              $ref: '#/components/schemas/RuntimePublication'
    PartnerWriteRequest:
      type: object
      required:
        - name
      description: Creates either a connected partner or a detached draft. When `as2ConnectionId` is non-null, ModernEDI validates the complete resolved X12 identities before committing.
      properties:
        name:
          type: string
          minLength: 1
        as2ConnectionId:
          type: integer
          minimum: 1
          nullable: true
        x12:
          $ref: '#/components/schemas/PartnerX12IdentityInputs'
        ediSupportEmail:
          type: string
          default: ''
        carbonCopyOnly:
          type: boolean
          default: false
        unmappedInboundPolicy:
          type: string
          enum:
            - MAPPED_ONLY
            - PASSTHROUGH_RAW_X12
          default: MAPPED_ONLY
        signingCertificateSlot:
          type: string
          enum:
            - active
            - next
          default: active
        supportsNewlineAfterSegmentTerminator:
          type: boolean
          default: false
    PartnerPatchRequest:
      type: object
      description: Any subset of writable partner fields. Omitted values remain unchanged.
      properties:
        name:
          type: string
          minLength: 1
        as2ConnectionId:
          type: integer
          minimum: 1
          nullable: true
        x12:
          $ref: '#/components/schemas/PartnerX12IdentityInputs'
        ediSupportEmail:
          type: string
        carbonCopyOnly:
          type: boolean
        unmappedInboundPolicy:
          type: string
          enum:
            - MAPPED_ONLY
            - PASSTHROUGH_RAW_X12
        signingCertificateSlot:
          type: string
          enum:
            - active
            - next
        supportsNewlineAfterSegmentTerminator:
          type: boolean
    PartnerCapabilitiesResponse:
      type: object
      required:
        - success
        - partner
        - capabilities
      properties:
        success:
          type: boolean
          enum:
            - true
        partner:
          $ref: '#/components/schemas/IntegrationPartner'
        capabilities:
          type: object
          required:
            - incoming
            - outgoing
          properties:
            incoming:
              type: array
              items:
                $ref: '#/components/schemas/PartnerMappingCapability'
            outgoing:
              type: array
              items:
                $ref: '#/components/schemas/PartnerMappingCapability'
    PartnerMappingCapability:
      type: object
      required:
        - mappingId
        - direction
        - x12Version
        - transactionSet
        - transformType
        - mappingSha256
        - published
      properties:
        mappingId:
          type: integer
        direction:
          type: string
          enum:
            - incoming
            - outgoing
        x12Version:
          type: string
          example: '4010'
        functionalGroupType:
          type: string
          nullable: true
          example: PO
        transactionSet:
          type: string
          example: '850'
        contentType:
          type: string
          nullable: true
          description: Source content type for outgoing maps or mapped-output content type for incoming maps.
          example: application/json
        transformType:
          type: string
          description: >
            Published transform implementation: `X12_MAPPER`, `XSLT`, or `JSLT`.
          example: JSLT
        purpose:
          type: string
          nullable: true
          allOf:
            - $ref: '#/components/schemas/MappingPurpose'
        mappingSha256:
          type: string
          description: Base64-encoded SHA-256 hash of the published map contents.
          example: 5vG51TfOLEo9YN0WSMf0bj+xzluq2UptTm4flVSo8EE=
        published:
          type: boolean
          enum:
            - true
    As2ProfileResponse:
      type: object
      required:
        - success
        - profile
      properties:
        success:
          type: boolean
          enum:
            - true
        profile:
          $ref: '#/components/schemas/As2Profile'
    As2Profile:
      type: object
      required:
        - domainName
        - environments
        - network
        - certificates
      properties:
        domainName:
          type: string
          nullable: true
          example: edi.customer.example
        environments:
          type: object
          required:
            - production
            - test
          properties:
            production:
              $ref: '#/components/schemas/As2EnvironmentProfile'
            test:
              $ref: '#/components/schemas/As2EnvironmentProfile'
        network:
          $ref: '#/components/schemas/As2NetworkProfile'
        certificates:
          type: object
          required:
            - active
            - next
          properties:
            active:
              type: object
              nullable: true
              allOf:
                - $ref: '#/components/schemas/As2PublicCertificate'
            next:
              type: object
              nullable: true
              allOf:
                - $ref: '#/components/schemas/As2PublicCertificate'
    As2EnvironmentProfile:
      type: object
      required:
        - as2Url
        - as2Identifier
        - x12Sender
      properties:
        as2Url:
          type: string
          format: uri
          nullable: true
        httpAs2Url:
          type: string
          format: uri
          description: Plain-HTTP endpoint when included in the tenant's plan and provisioned.
        as2Identifier:
          type: string
          nullable: true
        x12Sender:
          type: object
          required:
            - isaQualifier
            - isaId
            - gsId
          properties:
            isaQualifier:
              type: string
              nullable: true
            isaId:
              type: string
              nullable: true
            gsId:
              type: string
              nullable: true
    As2NetworkProfile:
      type: object
      required:
        - provisioningStatus
        - tlsCertificateStatus
        - staticInbound
        - staticOutbound
      properties:
        provisioningStatus:
          type: string
          nullable: true
        tlsCertificateStatus:
          type: string
          nullable: true
        staticInbound:
          $ref: '#/components/schemas/As2NetworkCapability'
        staticOutbound:
          $ref: '#/components/schemas/As2NetworkCapability'
    As2NetworkCapability:
      type: object
      required:
        - status
        - addresses
      properties:
        status:
          type: string
          enum:
            - ready
            - pending
            - not_included
        addresses:
          type: array
          items:
            type: string
            description: Public IPv4 or IPv6 address for partner allowlists.
    As2PublicCertificate:
      type: object
      required:
        - slot
        - publicCertPem
      properties:
        slot:
          type: string
          enum:
            - active
            - next
        subjectDn:
          type: string
          nullable: true
        issuerDn:
          type: string
          nullable: true
        serialNumber:
          type: string
          nullable: true
        fingerprintSha256:
          type: string
          nullable: true
        notBefore:
          $ref: '#/components/schemas/NullableUtcTimestamp'
        notAfter:
          $ref: '#/components/schemas/NullableUtcTimestamp'
        publicCertPem:
          type: string
          description: PEM-encoded public certificate safe to share with trading partners.
    As2ConnectionListResponse:
      type: object
      required:
        - success
        - connections
      properties:
        success:
          type: boolean
          enum:
            - true
        connections:
          type: array
          items:
            $ref: '#/components/schemas/As2ConnectionConfiguration'
    As2ConnectionResponse:
      type: object
      required:
        - success
        - connection
      properties:
        success:
          type: boolean
          enum:
            - true
        connection:
          $ref: '#/components/schemas/As2ConnectionConfiguration'
    As2ConnectionMutationResponse:
      allOf:
        - $ref: '#/components/schemas/As2ConnectionResponse'
        - type: object
          required:
            - runtimePublication
          properties:
            runtimePublication:
              $ref: '#/components/schemas/RuntimePublication'
    As2ConnectionConfiguration:
      allOf:
        - $ref: '#/components/schemas/As2ConnectionWriteRequest'
        - type: object
          required:
            - connectionId
            - environments
            - outbound
            - mdn
            - cmsAlgorithmProtection
            - digestCanonicalizationPolicy
            - etag
          properties:
            connectionId:
              type: integer
              minimum: 1
            etag:
              type: string
              description: Quoted SHA-256 configuration ETag.
    As2ConnectionWriteRequest:
      type: object
      required:
        - environments
      properties:
        environments:
          type: object
          required:
            - production
            - test
          properties:
            production:
              $ref: '#/components/schemas/As2PartnerEnvironment'
            test:
              $ref: '#/components/schemas/As2PartnerEnvironment'
        outbound:
          $ref: '#/components/schemas/As2OutboundTransportSettings'
        mdn:
          $ref: '#/components/schemas/As2MdnSettings'
        cmsAlgorithmProtection:
          type: boolean
          default: false
          description: Whether to include and require CMS algorithm-protection attributes for this connection.
        digestCanonicalizationPolicy:
          type: string
          enum:
            - AUTO
            - CANONICALIZE
            - PRESERVE
          default: AUTO
          description: How ModernEDI chooses the MIME body representation for digest calculation.
    As2ConnectionPatchRequest:
      type: object
      description: Any subset of AS2 connection fields. Nested omitted values remain unchanged.
      properties:
        environments:
          type: object
          properties:
            production:
              $ref: '#/components/schemas/As2PartnerEnvironmentPatch'
            test:
              $ref: '#/components/schemas/As2PartnerEnvironmentPatch'
        outbound:
          $ref: '#/components/schemas/As2OutboundTransportSettingsPatch'
        mdn:
          $ref: '#/components/schemas/As2MdnSettingsPatch'
        cmsAlgorithmProtection:
          type: boolean
        digestCanonicalizationPolicy:
          type: string
          enum:
            - AUTO
            - CANONICALIZE
            - PRESERVE
    As2PartnerEnvironment:
      type: object
      required:
        - as2Identifier
        - endpointUrl
        - publicCertificatePem
      properties:
        as2Identifier:
          type: string
          minLength: 1
          example: RETAILER_PROD
        endpointUrl:
          type: string
          format: uri
          maxLength: 2000
          description: Absolute HTTP or HTTPS partner destination URL without user information or a fragment. Endpoint workspaces must use HTTPS; plain HTTP requires Endpoint Plus, Static Outbound, Static Network, or Enterprise. The host must be publicly routable; local, private, link-local, multicast, carrier-grade NAT, documentation, benchmark, and other reserved address targets are rejected.
          example: https://as2.retailer.example/receive
        publicCertificatePem:
          type: string
          description: Exactly one PEM-encoded partner X.509 public certificate. ModernEDI canonicalizes it before storage.
          example: |-
            -----BEGIN CERTIFICATE-----
            MIIB...partner-public-certificate...
            -----END CERTIFICATE-----
    As2PartnerEnvironmentPatch:
      type: object
      properties:
        as2Identifier:
          type: string
          minLength: 1
        endpointUrl:
          type: string
          format: uri
          maxLength: 2000
          description: Absolute public HTTP or HTTPS partner destination URL without user information or a fragment. Endpoint workspaces must use HTTPS; plain HTTP requires Endpoint Plus, Static Outbound, Static Network, or Enterprise.
        publicCertificatePem:
          type: string
    As2OutboundTransportSettings:
      type: object
      required:
        - signingAlgorithm
        - encryptionAlgorithm
        - compressionAlgorithm
        - micAlgorithm
      properties:
        signingAlgorithm:
          type: string
          default: SHA256WITHRSA
          description: Supported AS2 signature algorithm enum name.
          example: SHA256WITHRSA
        encryptionAlgorithm:
          type: string
          default: DES_EDE3_CBC
          description: Supported AS2 content-encryption algorithm enum name.
          example: AES256_CBC
        compressionAlgorithm:
          type: string
          default: ZLIB
          example: ZLIB
        micAlgorithm:
          type: string
          enum:
            - SHA_1
            - SHA_256
            - SHA_512
            - MD5
          default: SHA_256
    As2OutboundTransportSettingsPatch:
      type: object
      properties:
        signingAlgorithm:
          type: string
        encryptionAlgorithm:
          type: string
        compressionAlgorithm:
          type: string
        micAlgorithm:
          type: string
          enum:
            - SHA_1
            - SHA_256
            - SHA_512
            - MD5
    As2MdnSettings:
      type: object
      required:
        - encrypt
        - messageStructure
        - micAlgorithmFormat
        - forceSynchronousForErrors
      properties:
        encrypt:
          type: boolean
          default: false
        messageStructure:
          type: string
          enum:
            - PLAIN
            - SIGNED
            - ENCRYPTED
            - SIGNED_ENCRYPTED
            - PLAIN_COMPRESSED
            - SIGNED_COMPRESSED
            - ENCRYPTED_COMPRESSED
            - ENCRYPTED_COMPRESSED_SIGNED
          default: SIGNED_ENCRYPTED
        micAlgorithmFormat:
          type: string
          enum:
            - RFC_5751
            - RFC_3851
          default: RFC_3851
        forceSynchronousForErrors:
          type: boolean
          default: true
          description: Request a synchronous MDN when error handling requires an immediate disposition.
    As2MdnSettingsPatch:
      type: object
      properties:
        encrypt:
          type: boolean
        messageStructure:
          type: string
          enum:
            - PLAIN
            - SIGNED
            - ENCRYPTED
            - SIGNED_ENCRYPTED
            - PLAIN_COMPRESSED
            - SIGNED_COMPRESSED
            - ENCRYPTED_COMPRESSED
            - ENCRYPTED_COMPRESSED_SIGNED
        micAlgorithmFormat:
          type: string
          enum:
            - RFC_5751
            - RFC_3851
        forceSynchronousForErrors:
          type: boolean
    GeneratedX12Request:
      type: object
      required:
        - x12
      properties:
        x12:
          type: string
          description: Complete X12 interchange to validate and send without mapping.
        businessKey:
          allOf:
            - $ref: '#/components/schemas/OutboundBusinessKey'
          description: Optional business identifier stored for transaction search and reconciliation.
    OutboundTransformEnvelope:
      type: object
      required:
        - contentType
        - input
      properties:
        contentType:
          type: string
          description: >
            Content type of `input` and the outgoing map to select. Use this
            envelope with HTTP `Content-Type:
            application/vnd.modernedi.outbound+json` when the source input is
            XML, text, or JSON with extra transform params. Legacy
            `application/json` envelopes containing `input` remain supported.
          example: application/xml
        input:
          description: >
            Source document to transform. For XML or text content types this
            must be a string. For JSON content types this may be a JSON object,
            array, string, number, boolean, or null.
          oneOf:
            - type: string
              nullable: true
            - type: object
              additionalProperties: true
            - type: array
              items: {}
            - type: number
            - type: boolean
        businessKey:
          allOf:
            - $ref: '#/components/schemas/OutboundBusinessKey'
          description: >
            Optional business identifier to store on the outbound transaction
            record, such as an invoice number, ASN number, BOL number, shipment
            id, or other value your system uses for reconciliation. This is
            recorded for transaction search and display only; it does not affect
            outgoing-map selection or transform execution.
        params:
          type: object
          additionalProperties: true
          description: >
            Optional transform parameters. For XSLT maps, each key is bound as
            an external stylesheet parameter with the same simple, unqualified
            name. `json` and `text` are reserved by ModernEDI. JSON
            strings map to `xs:string`, integers to integer numbers, decimals
            to decimal numbers, booleans to `xs:boolean`, null to an empty
            sequence, objects to `map(xs:string, item()*)`, and arrays to XDM
            sequences. Arrays of objects therefore work with declarations such
            as `<xsl:param name="pallets" as="map(xs:string, xs:anyAtomicType)*"
            required="yes"/>`. JSLT still uses `.` as its current input, but for
            JSLT maps ModernEDI evaluates the map with a root wrapper object
            shaped as `{ "input": <source>, "params": <params> }`. Source
            fields are therefore read as `.input.invoice.number` instead of
            directly from the root, and params are read as `.params.bolNumber`,
            `.params.pallets[0]`, and so on. When JSLT repeated output needs
            params inside a `for` loop,
            bind `.params` before the loop, such as `let params = .params`, and
            read `$params.bolNumber` inside the loop because `.` is the current
            source item there. Use `required="yes"` for XSLT params that every
            live request must supply. Params JSON files in the mapper editor are
            used only for test-running a map; they are not live defaults for this
            API. In the mapper editor, params are standalone JSON fixture files
            that can be edited like other workspace files and selected when
            test-running the map. While authoring JSLT, the editor uses the
            selected source and params fixture files for completions,
            diagnostics, and quick fixes for wrapper root mistakes, missing
            sample paths, and `get-key` object-key typos.
          example:
            bolNumber: BOL-DEMO-8842
            totalWeightInLbs: 1232.54
            transactionSetControlNumber: '000002321'
            pallets:
              - buyerSku: DEMO-SKU-001
                itemNumber: DEMO-ITEM-001
                numCases: 187
                sscc18: '000000000000000101'
    OutboundBusinessKey:
      type: object
      required:
        - name
        - value
      properties:
        name:
          type: string
          minLength: 1
          description: Stable business-key name, for example `invoiceNumber`, `shipmentId`, or `bolNumber`.
          example: shipmentId
        value:
          oneOf:
            - type: string
              minLength: 1
            - type: number
            - type: boolean
          description: Scalar business-key value. Numbers and booleans in the JSON envelope are accepted and stored as strings.
          example: ASN-DEMO-1001
    SendSuccessResponse:
      type: object
      required:
        - success
        - messageId
        - x12
      properties:
        success:
          type: boolean
          enum:
            - true
        messageId:
          type: string
          description: AS2 Message-Id assigned to the outbound message.
          example: <generated-message-id@modernedi>
        x12:
          type: string
          description: >
            Final X12 sent downstream after ModernEDI syntax-checks it and wraps
            it for AS2 delivery. For mapped endpoints this is generated output;
            for `/v1/as2/x12/...` endpoints it is the validated X12 supplied by
            the caller.
    OutboundPreviewResponse:
      type: object
      required:
        - success
        - generatedX12
        - validation
        - mapping
        - partner
        - businessKey
      properties:
        success:
          type: boolean
          enum:
            - true
        generatedX12:
          type: string
          description: Complete generated X12 that would be sent by `/v1/as2/send`.
        validation:
          $ref: '#/components/schemas/X12ValidationResult'
        mapping:
          type: object
          required:
            - id
            - transformType
            - file
            - fileSha256Hash
            - contentType
            - x12Version
          properties:
            id:
              type: integer
            transformType:
              type: string
              example: xslt
            file:
              type: string
            fileSha256Hash:
              type: string
              description: Base64-encoded SHA-256 hash of the published map contents.
              example: 5vG51TfOLEo9YN0WSMf0bj+xzluq2UptTm4flVSo8EE=
            contentType:
              type: string
            x12Version:
              type: string
        partner:
          type: object
          required:
            - partnerId
            - name
          properties:
            partnerId:
              type: integer
            name:
              type: string
        businessKey:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/OutboundBusinessKey'
    MappedOutputQueueResponse:
      type: object
      required:
        - success
        - environment
        - messages
        - limit
        - visibilityTimeoutSeconds
        - nextCursor
        - hasMore
      properties:
        success:
          type: boolean
          enum:
            - true
        environment:
          $ref: '#/components/schemas/TransactionEnvironmentValue'
        messages:
          type: array
          items:
            $ref: '#/components/schemas/MappedOutputMessage'
        limit:
          type: integer
          example: 10
        visibilityTimeoutSeconds:
          type: integer
          example: 120
        nextCursor:
          type: string
          nullable: true
          description: >
            Opaque position for the next bounded scan. Send this value as the
            next poll's `cursor` whenever `hasMore` is true.
        hasMore:
          type: boolean
          description: True when `nextCursor` should be used to continue scanning.
    MappedOutputMessage:
      type: object
      required:
        - id
        - receiptHandle
        - deliveryCount
        - environment
        - messageId
        - transactionKey
        - mappedOutputKey
        - sequenceNumber
        - purpose
        - partnerId
      properties:
        id:
          type: string
          description: >
            Stable id for this generated output version, used when marking the
            message received. A later durable regeneration can produce a new id
            for the same `messageId` and `mappedOutputKey`.
        receiptHandle:
          type: string
          description: Token from the latest poll response, required when marking the message received.
        deliveryCount:
          type: integer
          description: Number of pull or webhook delivery leases issued for this generated output version.
        leasedUntil:
          type: string
          format: date-time
          description: UTC instant when ModernEDI may return this message again if it has not been marked received.
          nullable: true
        messageId:
          type: string
          description: >
            Original inbound AS2 message id. Use this value as
            `originalMessageId` when sending a reply with `/v1/as2/reply`.
        transactionKey:
          type: string
          description: Transaction identifier within the AS2 message, usually `GS06#ST02`.
        partnerName:
          type: string
          nullable: true
        environment:
          $ref: '#/components/schemas/TransactionEnvironmentValue'
        partnerId:
          type: integer
          nullable: true
          description: Persisted tenant-scoped partner id that authorizes this transaction.
        transactionTimestamp:
          $ref: '#/components/schemas/NullableUtcTimestamp'
        transactionSet:
          $ref: '#/components/schemas/TransactionSet'
        controlNumbers:
          $ref: '#/components/schemas/TransactionControlNumbers'
        edi:
          $ref: '#/components/schemas/TransactionEdiMetadata'
        mapping:
          $ref: '#/components/schemas/MappedOutputMapping'
        mappedOutputKey:
          type: string
          description: Unique mapped-output key inside the transaction.
        sequenceNumber:
          type: integer
        purpose:
          $ref: '#/components/schemas/MappingPurpose'
        contentType:
          type: string
          nullable: true
          description: Media type for `payload`, such as `application/json` or `application/xml`.
          example: application/json
        createdAt:
          $ref: '#/components/schemas/NullableUtcTimestamp'
        businessKey:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/TransactionBusinessKey'
        extraFields:
          type: object
          description: >
            Delivered metadata selected by the incoming runtime map. ModernEDI's default incoming metadata set
            includes partner/customer identity, X12 version, transaction identifiers, sender ids, separators,
            timestamps, message ids, and live-map hash data. Custom delivered metadata keys are additive; setting
            a default key to `false` in the Mapper publish dialog intentionally omits it.
          additionalProperties:
            type: string
        payload:
          type: string
          nullable: true
          description: Mapped document body. Parse according to `contentType`.
    MappedOutputMapping:
      type: object
      properties:
        id:
          type: integer
          nullable: true
          example: 44
        type:
          type: string
          nullable: true
          example: ManagedQueueMapping
        file:
          type: string
          nullable: true
          example: customer-one-850.jslt
        fileSha256Hash:
          type: string
          nullable: true
          description: Base64-encoded SHA-256 hash of the published map contents.
          example: 5vG51TfOLEo9YN0WSMf0bj+xzluq2UptTm4flVSo8EE=
    MappedOutputAckRequest:
      type: object
      required:
        - receiptHandle
      properties:
        receiptHandle:
          type: string
          description: Token from the latest poll response for this mapped output id.
    MappedOutputAckResponse:
      type: object
      required:
        - success
        - environment
        - acknowledgment
        - id
        - messageId
        - mappedOutputKey
      properties:
        success:
          type: boolean
          enum:
            - true
        environment:
          $ref: '#/components/schemas/TransactionEnvironmentValue'
        acknowledgment:
          $ref: '#/components/schemas/MappedOutputAcknowledgment'
        id:
          type: string
          description: Mapped output id that was marked received.
        messageId:
          type: string
          description: Original inbound AS2 message id for the mapped output.
        mappedOutputKey:
          type: string
          description: Unique mapped-output key inside the transaction.
        ackedAt:
          $ref: '#/components/schemas/NullableUtcTimestamp'
          description: UTC instant when the mapped output was acknowledged.
    MappedOutputAcknowledgment:
      type: object
      required:
        - id
        - messageId
        - mappedOutputKey
      properties:
        id:
          type: string
        messageId:
          type: string
        mappedOutputKey:
          type: string
        ackedAt:
          $ref: '#/components/schemas/NullableUtcTimestamp'
          description: UTC instant when the mapped output was acknowledged.
    MappedOutputBulkAckRequest:
      type: object
      required:
        - acks
      properties:
        acks:
          type: array
          minItems: 1
          maxItems: 100
          items:
            type: object
            required:
              - id
              - receiptHandle
            properties:
              id:
                type: string
                description: Mapped-output id returned by the poll response.
              receiptHandle:
                type: string
                description: Receipt handle from the same poll response.
    MappedOutputBulkAckResponse:
      type: object
      required:
        - success
        - environment
        - acknowledgedCount
        - acknowledgments
      properties:
        success:
          type: boolean
          enum:
            - true
        environment:
          $ref: '#/components/schemas/TransactionEnvironmentValue'
        acknowledgedCount:
          type: integer
          minimum: 1
          maximum: 100
        acknowledgments:
          type: array
          items:
            $ref: '#/components/schemas/MappedOutputAcknowledgment'
    MappingListResponse:
      type: object
      required:
        - success
        - mappings
      properties:
        success:
          type: boolean
          enum:
            - true
        mappings:
          type: array
          items:
            $ref: '#/components/schemas/MappingConfiguration'
    MappingResponse:
      type: object
      required:
        - success
        - mapping
      properties:
        success:
          type: boolean
          enum:
            - true
        mapping:
          $ref: '#/components/schemas/MappingConfiguration'
    MappingMutationResponse:
      allOf:
        - $ref: '#/components/schemas/MappingResponse'
        - type: object
          required:
            - runtimePublication
          properties:
            runtimePublication:
              $ref: '#/components/schemas/RuntimePublication'
    MappingSourceVersionListResponse:
      type: object
      required:
        - success
        - mappingId
        - currentEtag
        - currentSourceVersion
        - nextCursor
        - hasMore
        - sourceVersions
      properties:
        success:
          type: boolean
          enum:
            - true
        mappingId:
          type: integer
          minimum: 1
        currentEtag:
          type: string
          description: Quoted ETag for the complete currently published mapping. Send this exact value in `If-Match` when restoring a source version.
        currentSourceVersion:
          allOf:
            - $ref: '#/components/schemas/MappingSourceVersionDetail'
          description: Complete currently published transform source from the same locked configuration snapshot as `currentEtag`. This is present even when its chronological source-version entry is outside the current page.
        nextCursor:
          type: string
          nullable: true
          description: Opaque cursor for the next chronological page, or `null` when no older source versions remain.
        hasMore:
          type: boolean
          description: Whether another page is available through `nextCursor`.
        sourceVersions:
          type: array
          description: One newest-first page of unique immutable transform-source summaries. Identical source text is represented once even if it was published more than once.
          items:
            $ref: '#/components/schemas/MappingSourceVersionSummary'
    MappingSourceVersionResponse:
      type: object
      required:
        - success
        - sourceVersion
      properties:
        success:
          type: boolean
          enum:
            - true
        sourceVersion:
          $ref: '#/components/schemas/MappingSourceVersionDetail'
    MappingSourceVersionSummary:
      type: object
      required:
        - sourceVersionId
        - createdAt
        - sourceHash
        - current
        - transform
      properties:
        sourceVersionId:
          type: integer
          minimum: 1
          description: Immutable id for this unique stored transform source.
        createdAt:
          $ref: '#/components/schemas/UtcTimestamp'
        sourceHash:
          type: string
          description: Base64-encoded SHA-256 hash of the transform source text. This identifies source content; it is not a concurrency-control ETag.
          example: 5vG51TfOLEo9YN0WSMf0bj+xzluq2UptTm4flVSo8EE=
        current:
          type: boolean
          description: Whether this source hash matches the mapping source that is currently published.
        transform:
          $ref: '#/components/schemas/MappingSourceVersionTransformSummary'
    MappingSourceVersionDetail:
      type: object
      required:
        - sourceVersionId
        - createdAt
        - sourceHash
        - current
        - transform
      properties:
        sourceVersionId:
          type: integer
          minimum: 1
          description: Immutable id for this unique stored transform source.
        createdAt:
          $ref: '#/components/schemas/UtcTimestamp'
        sourceHash:
          type: string
          description: Base64-encoded SHA-256 hash of the transform source text. This identifies source content; it is not a concurrency-control ETag.
          example: 5vG51TfOLEo9YN0WSMf0bj+xzluq2UptTm4flVSo8EE=
        current:
          type: boolean
          description: Whether this source hash matches the mapping source that is currently published.
        transform:
          $ref: '#/components/schemas/MappingSourceVersionTransformDetail'
    MappingSourceVersionTransformSummary:
      type: object
      required:
        - type
        - fileName
      properties:
        type:
          type: string
          enum:
            - X12_MAPPER
            - XSLT
            - JSLT
        fileName:
          type: string
          description: Transform filename recorded when this source was first archived. Restore keeps the currently published filename.
    MappingSourceVersionTransformDetail:
      type: object
      required:
        - type
        - fileName
        - source
      properties:
        type:
          type: string
          enum:
            - X12_MAPPER
            - XSLT
            - JSLT
        fileName:
          type: string
          description: Transform filename recorded when this source was first archived. Restore keeps the currently published filename.
        source:
          type: string
          description: Complete immutable historical transform source, suitable for previewing or diffing against the current mapping source.
    MappingConfiguration:
      type: object
      required:
        - mappingId
        - patchable
        - partnerId
        - direction
        - x12Version
        - functionalIdentifierCode
        - transactionSetIdentifierCode
        - transform
        - output
        - etag
      properties:
        mappingId:
          type: integer
          minimum: 1
        patchable:
          type: boolean
          readOnly: true
          description: Whether this mapping can be updated through the public Integration API. Legacy direct-to-infrastructure mapping types are returned with `false`.
        partnerId:
          type: integer
          minimum: 1
        direction:
          type: string
          enum:
            - INCOMING
            - OUTGOING
        x12Version:
          type: string
          description: Normalized X12 version, such as `4010`.
          example: '4010'
        functionalIdentifierCode:
          type: string
          description: X12 GS01 functional identifier enum name.
          example: PO
        transactionSetIdentifierCode:
          type: string
          description: X12 ST01 transaction-set identifier without a leading underscore.
          example: '850'
        transform:
          $ref: '#/components/schemas/MappingTransform'
        output:
          $ref: '#/components/schemas/MappingOutputConfiguration'
        businessKey:
          $ref: '#/components/schemas/MappingBusinessKey'
        activeAttributes:
          $ref: '#/components/schemas/MappingActiveAttributes'
        etag:
          type: string
          description: Quoted SHA-256 configuration ETag.
    MappingWriteRequest:
      oneOf:
        - $ref: '#/components/schemas/IncomingMappingWriteRequest'
        - $ref: '#/components/schemas/OutgoingMappingWriteRequest'
    IncomingMappingWriteRequest:
      type: object
      required:
        - partnerId
        - direction
        - x12Version
        - functionalIdentifierCode
        - transactionSetIdentifierCode
        - transform
        - output
        - businessKey
      properties:
        partnerId:
          type: integer
          minimum: 1
        direction:
          type: string
          enum:
            - INCOMING
        x12Version:
          type: string
        functionalIdentifierCode:
          type: string
        transactionSetIdentifierCode:
          type: string
        transform:
          allOf:
            - $ref: '#/components/schemas/MappingTransform'
            - type: object
              properties:
                type:
                  type: string
                  enum:
                    - X12_MAPPER
        output:
          $ref: '#/components/schemas/MappingOutput'
        businessKey:
          $ref: '#/components/schemas/MappingBusinessKey'
        activeAttributes:
          allOf:
            - $ref: '#/components/schemas/MappingActiveAttributes'
          description: Optional delivered-metadata overrides. Omitted keys inherit ModernEDI's safe runtime defaults.
    OutgoingMappingWriteRequest:
      type: object
      required:
        - partnerId
        - direction
        - x12Version
        - functionalIdentifierCode
        - transactionSetIdentifierCode
        - transform
        - output
      properties:
        partnerId:
          type: integer
          minimum: 1
        direction:
          type: string
          enum:
            - OUTGOING
        x12Version:
          type: string
        functionalIdentifierCode:
          type: string
        transactionSetIdentifierCode:
          type: string
        transform:
          allOf:
            - $ref: '#/components/schemas/MappingTransform'
            - type: object
              properties:
                type:
                  type: string
                  enum:
                    - XSLT
                    - JSLT
        output:
          allOf:
            - $ref: '#/components/schemas/MappingOutput'
            - type: object
              properties:
                purpose:
                  type: string
                  enum:
                    - PROCESSING
    MappingPatchRequest:
      type: object
      description: Deep partial mapping configuration. `mappingId`, `etag`, `patchable`, and `output.delivery` are server-managed and rejected in the body.
      properties:
        partnerId:
          type: integer
          minimum: 1
        direction:
          type: string
          enum:
            - INCOMING
            - OUTGOING
        x12Version:
          type: string
        functionalIdentifierCode:
          type: string
        transactionSetIdentifierCode:
          type: string
        transform:
          type: object
          properties:
            type:
              type: string
              enum:
                - X12_MAPPER
                - XSLT
                - JSLT
            fileName:
              type: string
              maxLength: 255
            source:
              type: string
              maxLength: 524288
        output:
          type: object
          properties:
            contentType:
              type: string
              maxLength: 255
            purpose:
              $ref: '#/components/schemas/MappingPurpose'
        businessKey:
          type: object
          properties:
            name:
              type: string
              maxLength: 128
            extractor:
              type: string
              maxLength: 4096
        activeAttributes:
          $ref: '#/components/schemas/MappingActiveAttributes'
    MappingTransform:
      type: object
      required:
        - type
        - fileName
        - source
      properties:
        type:
          type: string
          enum:
            - X12_MAPPER
            - XSLT
            - JSLT
        fileName:
          type: string
          minLength: 1
          maxLength: 255
          description: Source-controlled transform filename shown in the workspace and transaction provenance.
        source:
          type: string
          minLength: 1
          maxLength: 524288
          description: Complete map source text, limited to 524,288 characters (512 KiB) by the executable mapping runtime. This value is returned on reads for configuration-as-code workflows.
    MappingOutput:
      type: object
      required:
        - contentType
      properties:
        contentType:
          type: string
          minLength: 1
          maxLength: 255
          description: Mapped payload content type for incoming maps; source content type selected by outgoing maps.
        purpose:
          allOf:
            - $ref: '#/components/schemas/MappingPurpose'
          default: PROCESSING
          description: Defaults to `PROCESSING` when omitted. Incoming mappings may also use `ACKNOWLEDGMENT`; outgoing mappings use `PROCESSING`.
    MappingOutputConfiguration:
      allOf:
        - $ref: '#/components/schemas/MappingOutput'
        - type: object
          required:
            - delivery
          properties:
            delivery:
              allOf:
                - $ref: '#/components/schemas/MappingOutputDelivery'
              readOnly: true
              description: Server-selected delivery behavior. New incoming public mappings use `MAPPED_OUTPUTS`; outgoing maps use `OUTBOUND_AS2`.
    MappingOutputDelivery:
      type: string
      readOnly: true
      description: |
        Safe delivery category without infrastructure identifiers:

        - `MAPPED_OUTPUTS`: managed polling and per-partner webhook delivery
        - `TRANSACTION_RECORD`: incoming result retained on the transaction record
        - `EXTERNAL_SQS`: legacy direct SQS delivery; mapping is read-only publicly
        - `EXTERNAL_DYNAMODB`: legacy direct DynamoDB delivery; mapping is read-only publicly
        - `OUTBOUND_AS2`: outbound source-to-X12 transform used by AS2 send APIs
      enum:
        - MAPPED_OUTPUTS
        - TRANSACTION_RECORD
        - EXTERNAL_SQS
        - EXTERNAL_DYNAMODB
        - OUTBOUND_AS2
    MappingBusinessKey:
      type: object
      required:
        - name
        - extractor
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 128
          example: purchaseOrderNumber
        extractor:
          type: string
          minLength: 1
          maxLength: 4096
          description: Incoming X12 mapper expression used to persist the searchable business-key value.
          example: BEG03
    MappingActiveAttributes:
      type: object
      maxProperties: 64
      description: Effective mapped-output metadata flags. Attribute names may contain at most 128 Unicode characters and values must be booleans.
      additionalProperties:
        type: boolean
    TransactionListResponse:
      type: object
      required:
        - success
        - environment
        - transactions
        - hasMore
        - window
        - attentionSummary
      properties:
        success:
          type: boolean
          enum:
            - true
        environment:
          $ref: '#/components/schemas/TransactionEnvironmentValue'
        transactions:
          type: array
          items:
            $ref: '#/components/schemas/TransactionSummary'
        nextCursor:
          type: string
          nullable: true
        hasMore:
          type: boolean
        window:
          $ref: '#/components/schemas/TransactionWindow'
        attentionSummary:
          $ref: '#/components/schemas/TransactionAttentionSummary'
    TransactionDetailResponse:
      type: object
      required:
        - success
        - environment
        - transaction
      properties:
        success:
          type: boolean
          enum:
            - true
        environment:
          $ref: '#/components/schemas/TransactionEnvironmentValue'
        transaction:
          $ref: '#/components/schemas/TransactionDetail'
    TransactionDocumentsResponse:
      type: object
      required:
        - success
        - environment
        - documents
      properties:
        success:
          type: boolean
          enum:
            - true
        environment:
          $ref: '#/components/schemas/TransactionEnvironmentValue'
        documents:
          type: array
          items:
            $ref: '#/components/schemas/TransactionDocument'
    TransactionEventsResponse:
      type: object
      required:
        - success
        - environment
        - events
      properties:
        success:
          type: boolean
          enum:
            - true
        environment:
          $ref: '#/components/schemas/TransactionEnvironmentValue'
        events:
          type: array
          items:
            $ref: '#/components/schemas/TransactionEvent'
    RelatedTransactionsResponse:
      type: object
      required:
        - success
        - environment
        - transaction
        - relatedTransactions
      properties:
        success:
          type: boolean
          enum:
            - true
        environment:
          $ref: '#/components/schemas/TransactionEnvironmentValue'
        transaction:
          $ref: '#/components/schemas/TransactionSummary'
        relatedTransactions:
          type: array
          items:
            $ref: '#/components/schemas/TransactionDetail'
    TransactionWindow:
      type: object
      required:
        - startDate
        - endDate
        - timeZone
      properties:
        startDate:
          $ref: '#/components/schemas/UtcTimestamp'
        endDate:
          $ref: '#/components/schemas/UtcTimestamp'
        timeZone:
          type: string
          enum:
            - UTC
          description: Time zone used for transaction timestamps and normalized query windows.
    TransactionAttentionReason:
      type: string
      enum:
        - mapping_failure
        - functional_ack_issue
        - mapped_output_delivery
        - watchlist
      description: >
        Machine-readable reason that a transaction needs operator attention.
        One transaction can have more than one active reason.
    TransactionAttentionSummary:
      type: object
      required:
        - needsAttentionCount
        - reasonCounts
        - freshness
      description: >
        Environment-wide operator-inbox summary. Counts are independent of the
        transaction list's date window, row limit, cursor, and other filters.
        A transaction with more than one reason is counted once in
        `needsAttentionCount` and once in each applicable reason count.
      properties:
        needsAttentionCount:
          type: integer
          minimum: 0
          description: Distinct transactions currently projected as needing attention.
        reasonCounts:
          type: object
          required:
            - mappingFailure
            - functionalAckIssue
            - mappedOutputDelivery
            - watchlist
          properties:
            mappingFailure:
              type: integer
              minimum: 0
            functionalAckIssue:
              type: integer
              minimum: 0
            mappedOutputDelivery:
              type: integer
              minimum: 0
            watchlist:
              type: integer
              minimum: 0
        freshness:
          $ref: '#/components/schemas/TransactionAttentionFreshness'
    TransactionAttentionFreshness:
      type: object
      required:
        - status
        - complete
        - lastCompletedAt
        - reconciliationStartedAt
        - message
      description: >
        Reconciliation state for the selected environment. Until the first
        complete sweep, filtered rows and counts can be partial or empty;
        `complete: false` means an empty result is not proof of zero. An
        incomplete or failed sweep does not clear previously observed active
        attention items.
      properties:
        status:
          type: string
          enum:
            - pending
            - reconciling
            - current
            - stale
        complete:
          type: boolean
          description: >
            Whether the currently reported projection is complete and current.
            This is false while the first sweep is pending, while a refresh is
            reconciling, or when reconciliation is stale; previously observed
            active items remain visible during those states.
        lastCompletedAt:
          type: string
          format: date-time
          nullable: true
        reconciliationStartedAt:
          type: string
          format: date-time
          nullable: true
        message:
          type: string
          nullable: true
          description: Safe operator-facing freshness or retry guidance; internal errors are not exposed.
    TransactionMappingStatusSummary:
      type: object
      required:
        - status
        - latestAttemptAt
        - failureCount
      properties:
        status:
          type: string
          enum:
            - NOT_RECORDED
            - UNAVAILABLE
            - SUCCEEDED
            - FAILED
            - COMPLETED_WITH_ERRORS
            - RECOVERED
          description: >
            Consolidated mapping outcome. `NOT_RECORDED` means no mapping runtime
            result was found; it does not claim that a map ran successfully.
            `UNAVAILABLE` means mapping telemetry could not be read while this
            response was built. It is fail-soft, has `failureCount: 0`, and does
            not by itself set `needsAttention`; retry before drawing a conclusion.
            `RECOVERED` means earlier failures were resolved by a later successful
            attempt. `COMPLETED_WITH_ERRORS` means at least one attempt succeeded
            while another failure remains unresolved.
        latestAttemptAt:
          type: string
          format: date-time
          nullable: true
          description: Timestamp of the latest mapping attempt or fallback post-process status.
        failureCount:
          type: integer
          minimum: 0
          description: Number of unresolved mapping failures for this transaction.
    TransactionMappingStatus:
      allOf:
        - $ref: '#/components/schemas/TransactionMappingStatusSummary'
        - type: object
          required:
            - attempts
          properties:
            attempts:
              type: array
              description: Mapping attempts in reverse chronological order. This is empty when only fallback post-process status is available.
              items:
                $ref: '#/components/schemas/TransactionMappingAttempt'
    TransactionMappingAttempt:
      type: object
      required:
        - failureId
        - attemptId
        - messageId
        - transactionKey
        - sourceTransactionKey
        - direction
        - environment
        - currentRevision
        - stage
        - status
        - code
        - fallback
        - timestamp
        - resolved
      properties:
        failureId:
          type: string
          description: Opaque identifier for correlating this failure in support and replay diagnostics.
        attemptId:
          type: string
        messageId:
          type: string
        transactionKey:
          type: string
          description: Internal mapping-attempt transaction key.
        sourceTransactionKey:
          type: string
          description: Transaction key of the source transaction shown in the transaction viewer.
        direction:
          type: string
          enum: [inbound, outbound]
        environment:
          $ref: '#/components/schemas/TransactionEnvironmentValue'
        partnerName:
          type: string
          nullable: true
        partnerId:
          type: integer
          nullable: true
        mappingId:
          type: integer
          nullable: true
        mapFile:
          type: string
          nullable: true
        mapFileSha256Hash:
          type: string
          nullable: true
        currentMapFileSha256Hash:
          type: string
          nullable: true
        currentRevision:
          type: boolean
          description: Whether the attempted map hash is still the currently published revision.
        transactionSetIdentifierCode:
          type: string
          nullable: true
        transactionControlNumber:
          type: string
          nullable: true
        functionalIdentifierCode:
          type: string
          nullable: true
        functionalGroupControlNumber:
          type: string
          nullable: true
        x12Version:
          type: string
          nullable: true
        stage:
          type: string
          description: Mapping pipeline stage that produced the result.
        status:
          type: string
          enum: [FAILED, SUCCEEDED]
        code:
          type: string
          description: Stable machine-readable mapping result code.
        safeMessage:
          type: string
          nullable: true
          description: Sanitized diagnostic message safe to display to workspace operators.
        line:
          type: integer
          nullable: true
        column:
          type: integer
          nullable: true
        fallback:
          type: string
          description: Fallback behavior used after this attempt, or `NONE`.
        timestamp:
          type: string
          format: date-time
        replayId:
          type: string
          nullable: true
        replayOfAttemptId:
          type: string
          nullable: true
        resolved:
          type: boolean
        resolvedAt:
          type: string
          format: date-time
          nullable: true
        resolvedByAttemptId:
          type: string
          nullable: true
    TransactionDetail:
      type: object
      required:
        - needsAttention
        - attentionReasons
        - mappingStatus
        - summary
        - mappedOutputs
        - x12
        - mdn
        - functionalAck
        - technicalAck
        - documents
        - events
        - relatedTransactions
      properties:
        needsAttention:
          type: boolean
          description: >
            True when the transaction has an active operational watch, an
            unresolved mapping failure, or a functional acknowledgment outcome
            other than `accepted`, or when a managed-output lease expired before
            acknowledgment or an unacknowledged output was redelivered. A newly
            ready output and an active first lease are normal handoff progress
            and do not set this signal by themselves.
        attentionReasons:
          type: array
          uniqueItems: true
          description: Active machine-readable reasons behind `needsAttention`.
          items:
            $ref: '#/components/schemas/TransactionAttentionReason'
        mappingStatus:
          $ref: '#/components/schemas/TransactionMappingStatus'
        summary:
          $ref: '#/components/schemas/TransactionSummary'
        mappedOutputs:
          type: array
          description: Inbound mapped outputs. Outbound reply transactions return an empty array.
          items:
            $ref: '#/components/schemas/StoredTransactionMappedOutput'
        x12:
          type: string
          nullable: true
          description: Stored X12 payload for the selected transaction, when available.
        mdn:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/TransactionMdn'
        functionalAck:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/FunctionalAcknowledgmentDetail'
        technicalAck:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/TechnicalAcknowledgmentDetail'
        documents:
          type: array
          items:
            $ref: '#/components/schemas/TransactionDocument'
        events:
          type: array
          items:
            $ref: '#/components/schemas/TransactionEvent'
        relatedTransactions:
          type: array
          description: Outbound replies linked to this inbound transaction.
          items:
            $ref: '#/components/schemas/TransactionDetail'
    TransactionSummary:
      type: object
      required:
        - id
        - messageId
        - transactionKey
        - direction
        - partnerName
        - partnerId
        - timestamp
        - transactionSet
        - controlNumbers
        - edi
        - businessKey
        - environment
        - needsAttention
        - attentionReasons
        - mappingStatus
        - functionalAckOutcome
      properties:
        id:
          type: string
          description: Convenience id formatted as `<messageId>/<transactionKey>`.
          example: msg-850-api/17#0001
        environment:
          $ref: '#/components/schemas/TransactionEnvironmentValue'
        needsAttention:
          type: boolean
          description: >
            Stable operator-inbox signal. True when an unexpired watchlist entry
            is active, a mapping failure remains unresolved, or
            `functionalAckOutcome.status` is present and is not `accepted`, or
            when a managed-output lease expired before acknowledgment or an
            unacknowledged output was redelivered. A newly ready output and an
            active first lease are normal handoff progress and do not set this
            signal by themselves.
        attentionReasons:
          type: array
          uniqueItems: true
          description: Active machine-readable reasons behind `needsAttention`.
          items:
            $ref: '#/components/schemas/TransactionAttentionReason'
        mappingStatus:
          $ref: '#/components/schemas/TransactionMappingStatusSummary'
        messageId:
          type: string
          description: AS2 Message-Id for this transaction.
          example: msg-850-api
        transactionKey:
          type: string
          description: Transaction identifier within the AS2 message, usually `GS06#ST02`.
          example: 17#0001
        replyToMessageId:
          type: string
          nullable: true
          description: Present on outbound replies and points back to the inbound AS2 message id.
        direction:
          type: string
          enum:
            - inbound
            - outbound
          example: inbound
        partnerName:
          type: string
          example: Customer One
        partnerId:
          type: integer
          nullable: true
          description: Persisted tenant-scoped partner id that authorizes this transaction and remains stable across partner renames.
          example: 1
        timestamp:
          $ref: '#/components/schemas/UtcTimestamp'
        transactionSet:
          $ref: '#/components/schemas/TransactionSet'
        controlNumbers:
          $ref: '#/components/schemas/TransactionControlNumbers'
        edi:
          $ref: '#/components/schemas/TransactionEdiMetadata'
        businessKey:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/TransactionBusinessKey'
          description: >
            Business key recorded on this transaction. Inbound keys come from
            the incoming map. Outbound keys are present when `/send`, `/reply`,
            or `/carbonCopy` supplied `businessKey` in the JSON request envelope;
            otherwise this is `null`.
        functionalAckOutcome:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/FunctionalAcknowledgmentSummary'
          description: >
            Compact normalized 997 result. A null value means no compact outcome was available, normally because
            no 997 is stored; list enrichment is fail-soft, so callers should use transaction detail before treating
            null as proof that no acknowledgment exists.
    TransactionSet:
      type: object
      properties:
        code:
          type: string
          nullable: true
          example: '850'
        description:
          type: string
          nullable: true
          example: Purchase Order
    TransactionControlNumbers:
      type: object
      properties:
        transaction:
          type: string
          nullable: true
          description: ST02 transaction control number.
          example: '0001'
        functionalGroup:
          type: string
          nullable: true
          description: GS06 functional group control number.
          example: '17'
    TransactionEdiMetadata:
      type: object
      properties:
        x12Version:
          type: string
          nullable: true
          example: '4010'
        functionalIdentifierCode:
          type: string
          nullable: true
          example: PO
        transactionGroupType:
          type: string
          nullable: true
          description: ST01 transaction set identifier code recorded by the runtime.
          example: '850'
        segmentTerminator:
          type: string
          nullable: true
          example: '~'
    TransactionBusinessKey:
      type: object
      properties:
        name:
          type: string
          nullable: true
          example: purchaseOrderNumber
        value:
          type: string
          example: PO-100045
    TransactionMappedOutput:
      type: object
      required:
        - id
        - sequenceNumber
        - purpose
      properties:
        id:
          type: string
          description: >
            Artifact identifier within the transaction response, such as
            `mapped-output-1`. This is not the managed-queue id and cannot be
            used with `/v1/mapped-outputs/{id}/ack`. Stored transaction details
            expose the queue correlation id separately as `delivery.outputId`.
          example: mapped-output-1
        sequenceNumber:
          type: integer
          example: 1
        purpose:
          $ref: '#/components/schemas/MappingPurpose'
        contentType:
          type: string
          nullable: true
          example: application/json
        createdAt:
          $ref: '#/components/schemas/NullableUtcTimestamp'
        businessKey:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/TransactionBusinessKey'
        mapping:
          $ref: '#/components/schemas/TransactionMappedOutputMapping'
        extraFields:
          type: object
          additionalProperties:
            type: string
        payload:
          type: string
          nullable: true
          description: Mapped output body. Parse according to `contentType`.
    StoredTransactionMappedOutput:
      description: >
        Mapped output persisted on a transaction detail. Unlike a response-only
        replay output, a stored output always includes its delivery applicability
        and current handoff state.
      allOf:
        - $ref: '#/components/schemas/TransactionMappedOutput'
        - type: object
          required:
            - delivery
          properties:
            delivery:
              $ref: '#/components/schemas/TransactionMappedOutputDelivery'
    TransactionMappedOutputDelivery:
      type: object
      description: >
        Handoff state for a mapped output stored on a transaction. This object
        is present on stored transaction-detail mapped outputs and omitted from
        response-only replay results, which do not enter the managed queue.
        `acknowledged` means acknowledged by your integration through the queue
        acknowledgment API or a successful mapped-output webhook; it does not
        prove how another application handled the document afterward.
      required:
        - applicability
        - status
        - outputId
        - deliveryCount
        - redelivered
        - firstDeliveredAt
        - lastDeliveredAt
        - leasedUntil
        - ackedAt
      properties:
        applicability:
          type: string
          enum:
            - managed_queue
            - not_applicable
            - untracked
          description: >
            `managed_queue` exposes a current polling/webhook state;
            `not_applicable` is a transaction-record-only artifact; `untracked`
            is a legacy external delivery whose downstream state ModernEDI does
            not record in the managed queue.
        status:
          type: string
          nullable: true
          enum:
            - ready
            - leased
            - acknowledged
          description: >
            Current managed-queue state, or null when applicability is not
            `managed_queue`. `ready` includes a never-leased output and an output
            whose prior lease expired. `leased` means the visibility timeout is
            still active. `acknowledged` means acknowledged by your integration.
        outputId:
          type: string
          nullable: true
          description: >
            Managed-queue correlation id for this generated output, or null when
            queue tracking does not apply. It is distinct from the mapped output's
            top-level transaction artifact `id`. It stays stable across ordinary
            processing retries that reproduce the same logical output. A successful
            durable replay creates a new id even when the regenerated content is
            identical. Acknowledgment still requires the latest `receiptHandle`
            returned by `GET /v1/mapped-outputs`; transaction detail does not provide
            that receipt handle.
        deliveryCount:
          type: integer
          minimum: 0
          nullable: true
          description: >
            Number of times the output has been leased for pull or webhook delivery
            attempts, or null when queue tracking does not apply. This count does
            not imply successful downstream processing.
        redelivered:
          type: boolean
          nullable: true
          description: True after more than one lease or delivery attempt; independent of the current status.
        firstDeliveredAt:
          description: UTC instant when the output was first leased for a pull or webhook delivery attempt.
          allOf:
            - $ref: '#/components/schemas/NullableUtcTimestamp'
        lastDeliveredAt:
          description: UTC instant when the output was most recently leased for a pull or webhook delivery attempt.
          allOf:
            - $ref: '#/components/schemas/NullableUtcTimestamp'
        leasedUntil:
          description: UTC instant when the current visibility lease expires, or null when no lease has been issued.
          allOf:
            - $ref: '#/components/schemas/NullableUtcTimestamp'
        ackedAt:
          description: >
            UTC instant when ModernEDI recorded acknowledgment by the customer's
            integration or a successful webhook response. This does not prove
            later ERP or business processing.
          allOf:
            - $ref: '#/components/schemas/NullableUtcTimestamp'
    TransactionMappedOutputMapping:
      type: object
      properties:
        id:
          type: integer
          nullable: true
          example: 44
        type:
          type: string
          nullable: true
          example: ManagedQueueMapping
        file:
          type: string
          nullable: true
          example: customer-one-850.jslt
        fileSha256Hash:
          type: string
          nullable: true
          description: Base64-encoded SHA-256 hash of the map file used when the inbound output was produced.
        currentFileSha256Hash:
          type: string
          nullable: true
          description: Base64-encoded SHA-256 hash of the currently published map file, when it can be resolved.
        versionStatus:
          type: string
          nullable: true
          enum:
            - current
            - changed
            - missing
            - unknown
          example: current
    MappingPurpose:
      type: string
      description: >
        Purpose of the runtime map that produced this output. `PROCESSING` means the output is for the normal
        application workflow. `ACKNOWLEDGMENT` means the output is an automatically generated response document.
      enum:
        - PROCESSING
        - ACKNOWLEDGMENT
      example: PROCESSING
    TransactionMdn:
      type: object
      properties:
        messageId:
          type: string
          nullable: true
          example: mdn-850-api
        disposition:
          type: string
          nullable: true
          example: processed
        report:
          type: string
          nullable: true
        sent:
          type: boolean
          nullable: true
          description: True when this tenant sent the MDN; false when it received it.
        timestamp:
          $ref: '#/components/schemas/NullableUtcTimestamp'
          description: Stored MDN timestamp used by the event timeline.
    FunctionalAcknowledgmentDetail:
      type: object
      description: Stored X12 997 plus a normalized outcome derived from its AK9 and AK2/AK5 segments.
      properties:
        messageId:
          type: string
          nullable: true
          example: fa-850-api
        sent:
          type: boolean
          nullable: true
        x12:
          type: string
          nullable: true
        timestamp:
          $ref: '#/components/schemas/NullableUtcTimestamp'
          description: Stored functional-acknowledgment timestamp used by the event timeline.
        mdn:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/TransactionMdn'
        outcome:
          $ref: '#/components/schemas/FunctionalAcknowledgmentOutcome'
    FunctionalAcknowledgmentOutcome:
      type: object
      description: >
        Normalized 997 result. `status` is determined by AK901 and is not inferred from AK5 alone, but a
        contradictory AK5/count rollup is reported as unknown rather than trusting an internally inconsistent
        997. Whenever `status` is `unknown`, inspect `unknownReason` and the original `functionalAck.x12`
        value. A false `parsed` value indicates a structural failure; an unsupported AK901, AK501, or syntax-error
        code can be structurally parsed while its status remains unknown. The `unsupported_acknowledgment_code`
        reason does not by itself prove that AK901 was unsupported. Only `accepted` is a clean automation completion.
        `accepted_with_errors` and `unknown` require review; `partially_accepted` and `rejected` are failure or
        rejection outcomes that require action.
      required:
        - status
        - summary
        - parsed
        - errorCodes
        - transactionSets
      properties:
        status:
          $ref: '#/components/schemas/FunctionalAcknowledgmentStatus'
        acknowledgmentCode:
          type: string
          nullable: true
          description: Original AK901 functional-group acknowledgment code, when available.
          example: A
        summary:
          type: string
          description: Human-readable group result suitable for a transaction timeline or status panel.
          example: Functional group accepted; 1 of 1 received transaction set was accepted.
        parsed:
          type: boolean
          description: True when AK9 and all three AK902-AK904 counts were structurally valid.
        unknownReason:
          type: string
          nullable: true
          description: Machine-readable reason when `status` is `unknown`.
          enum:
            - missing_x12
            - not_997
            - invalid_structure
            - missing_ak9
            - invalid_ak9
            - unsupported_acknowledgment_code
            - inconsistent_rollup
        includedTransactionSets:
          type: integer
          minimum: 0
          nullable: true
          description: AK902, the number of transaction sets included in the functional group.
        receivedTransactionSets:
          type: integer
          minimum: 0
          nullable: true
          description: AK903, the number of transaction sets received.
        acceptedTransactionSets:
          type: integer
          minimum: 0
          nullable: true
          description: AK904, the number of received transaction sets accepted.
        errorCodes:
          type: array
          description: Non-empty AK905 and later functional-group syntax error codes.
          items:
            type: string
        transactionSets:
          type: array
          description: >
            Transaction-set results from paired AK2 and AK5 segments. Each result repeats its parent AK101/AK102
            identity so flattened results remain attributable when an interchange contains multiple 997 groups.
          items:
            $ref: '#/components/schemas/FunctionalAcknowledgmentTransactionSetOutcome'
    FunctionalAcknowledgmentTransactionSetOutcome:
      type: object
      required:
        - functionalIdentifierCode
        - functionalGroupControlNumber
        - status
        - errorCodes
      properties:
        functionalIdentifierCode:
          type: string
          nullable: true
          description: AK101 functional identifier code for the acknowledged group, such as `PO` or `IN`.
          example: PO
        functionalGroupControlNumber:
          type: string
          nullable: true
          description: AK102 control number for the acknowledged functional group.
          example: '17'
        transactionSetIdentifierCode:
          type: string
          nullable: true
          description: AK201 transaction set identifier code, such as `850` or `810`.
          example: '850'
        transactionSetControlNumber:
          type: string
          nullable: true
          description: AK202 transaction set control number.
          example: '0001'
        status:
          $ref: '#/components/schemas/FunctionalAcknowledgmentStatus'
        acknowledgmentCode:
          type: string
          nullable: true
          description: Original AK501 transaction-set acknowledgment code, when available.
          example: A
        errorCodes:
          type: array
          description: Non-empty AK502 and later transaction-set syntax error codes.
          items:
            type: string
    FunctionalAcknowledgmentSummary:
      type: object
      description: >
        Compact 997 result included on transaction summaries for list-level monitoring. Only `accepted` is a
        clean automation completion. `accepted_with_errors` and `unknown` require review; `partially_accepted`
        and `rejected` are failure or rejection outcomes that require action.
      required:
        - status
        - summary
      properties:
        status:
          $ref: '#/components/schemas/FunctionalAcknowledgmentStatus'
        acknowledgmentCode:
          type: string
          nullable: true
          description: Original AK901 functional-group acknowledgment code, when available.
          example: A
        summary:
          type: string
          description: Human-readable functional-group result.
          example: Functional group accepted; 1 of 1 received transaction set was accepted.
    FunctionalAcknowledgmentStatus:
      type: string
      description: Stable normalized acknowledgment status used for group and transaction-set results.
      enum:
        - accepted
        - accepted_with_errors
        - partially_accepted
        - rejected
        - unknown
      example: accepted
    TechnicalAcknowledgmentDetail:
      type: object
      properties:
        sent:
          type: boolean
          nullable: true
        x12:
          type: string
          nullable: true
        timestamp:
          $ref: '#/components/schemas/NullableUtcTimestamp'
        responseStatusCode:
          type: integer
          nullable: true
          example: 200
        responseBody:
          type: string
          nullable: true
    TransactionDocument:
      type: object
      required:
        - id
        - type
        - label
        - contentType
        - encoding
        - contentLength
        - content
        - metadata
      properties:
        id:
          type: string
          example: raw-x12
        type:
          type: string
          enum:
            - x12
            - mapped_output
            - mdn
            - functional_ack
            - technical_ack
            - http_response
          example: x12
        label:
          type: string
          example: Inbound X12
        contentType:
          type: string
          example: application/edi-x12
        encoding:
          type: string
          enum:
            - text
        timestamp:
          $ref: '#/components/schemas/NullableUtcTimestamp'
        contentLength:
          type: integer
          example: 256
        content:
          type: string
          description: Document body. Parse according to `contentType`.
        metadata:
          type: object
          additionalProperties: true
    TransactionEvent:
      type: object
      required:
        - id
        - type
        - label
        - details
      properties:
        id:
          type: string
          example: transaction
        type:
          type: string
          example: transaction.received
          description: >
            Event type includes direction, for example `mdn.sent` or
            `mdn.received`, `functional_ack.sent` or
            `functional_ack.received`, and `technical_ack.sent` or
            `technical_ack.received`.
        label:
          type: string
          example: Transaction received
        timestamp:
          $ref: '#/components/schemas/NullableUtcTimestamp'
          description: Stored timestamp for the underlying transaction, mapping, MDN, or acknowledgment.
        details:
          type: object
          additionalProperties: true
    TransactionEnvironmentValue:
      type: string
      description: Isolated transaction and queue environment used for this response object.
      enum:
        - production
        - test
      example: production
    UtcTimestamp:
      type: string
      format: date-time
      pattern: '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{9}Z$'
      description: UTC RFC 3339 instant with exactly nine fractional digits and a trailing `Z`.
      example: '2026-07-15T18:30:00.000000000Z'
    NullableUtcTimestamp:
      type: string
      format: date-time
      pattern: '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{9}Z$'
      nullable: true
      description: UTC RFC 3339 instant with exactly nine fractional digits and a trailing `Z`, or `null`.
      example: '2026-07-15T18:30:00.000000000Z'
    TransactionReplayRequest:
      type: object
      additionalProperties: false
      properties:
        environment:
          $ref: '#/components/schemas/TransactionEnvironmentValue'
        mode:
          type: string
          enum:
            - current_maps
          default: current_maps
          description: Uses the mappings currently published for the partner. This is the only public replay mode.
        delivery:
          type: string
          enum:
            - response_only
          default: response_only
          description: Regenerated outputs are returned only; no queue item, webhook, or outbound acknowledgment is produced.
        reason:
          type: string
          maxLength: 500
          nullable: true
          description: Optional operator or automation context recorded on the replay attempt.
    TransactionReplayResponse:
      type: object
      required:
        - success
        - replay
        - regeneratedOutputs
      properties:
        success:
          type: boolean
          enum:
            - true
        replay:
          $ref: '#/components/schemas/TransactionReplay'
        regeneratedOutputs:
          type: array
          description: Newly generated map results. They are not enqueued and do not trigger mapped-output webhooks.
          items:
            $ref: '#/components/schemas/TransactionMappedOutput'
    TransactionReplay:
      type: object
      required:
        - id
        - status
        - environment
        - messageId
        - transactionKey
        - mode
        - delivery
        - reason
        - requestedByApiKeyId
        - attempts
        - requestedAt
        - startedAt
        - completedAt
        - error
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          enum:
            - succeeded
            - processing
            - failed
        environment:
          $ref: '#/components/schemas/TransactionEnvironmentValue'
        messageId:
          type: string
        transactionKey:
          type: string
        mode:
          type: string
          enum:
            - current_maps
        delivery:
          type: string
          enum:
            - response_only
        reason:
          type: string
          nullable: true
        requestedByApiKeyId:
          type: integer
          minimum: 1
        attempts:
          type: integer
          minimum: 1
        requestedAt:
          $ref: '#/components/schemas/UtcTimestamp'
        startedAt:
          $ref: '#/components/schemas/NullableUtcTimestamp'
        completedAt:
          $ref: '#/components/schemas/NullableUtcTimestamp'
        error:
          type: object
          nullable: true
          properties:
            code:
              type: string
              nullable: true
            message:
              type: string
              nullable: true
    MappedOutputWebhookUpdateRequest:
      type: object
      additionalProperties: false
      required:
        - enabled
      properties:
        enabled:
          type: boolean
        endpointUrl:
          type: string
          format: uri
          maxLength: 2000
          nullable: true
          description: Required when `enabled` is true. Must be an absolute HTTPS URL without user information or a fragment and use a publicly routable host. DNS is revalidated before every delivery.
          example: https://erp.example.com/webhooks/modernedi
        rotateSigningSecret:
          type: boolean
          default: false
    MappedOutputWebhookUpdateResponse:
      type: object
      required:
        - success
        - webhook
      properties:
        success:
          type: boolean
          enum:
            - true
        webhook:
          $ref: '#/components/schemas/MappedOutputWebhookStatus'
        plainTextSigningSecret:
          type: string
          description: Present only when a signing secret was created or rotated. It is never returned again.
          example: medo_whsec_qQ2h3zvT1vD1Vh0CG0q2gQ
        message:
          type: string
          description: Present with `plainTextSigningSecret` to remind the caller to save it.
    RuntimePublication:
      type: object
      required:
        - state
        - retrying
      description: Publication state for a committed partner, AS2 connection, or mapping mutation. This field is not returned by reads or webhook configuration changes.
      properties:
        state:
          type: string
          enum:
            - published
            - pending
            - not_provisioned
          description: |
            - `published`: the tenant runtime configuration was published successfully, either inline during rollout or by updating its external configuration pointer.
            - `pending`: the database mutation committed, but runtime publication failed. ModernEDI's reconciler retries automatically every 60 seconds.
            - `not_provisioned`: the tenant does not have runtime infrastructure yet, so there is no runtime context to publish.
        retrying:
          type: boolean
          description: True when the 60-second reconciler will continue retrying a pending publication.
        message:
          type: string
          description: Optional safe operational context when publication is pending or not provisioned.
      example:
        state: published
        retrying: false
    MappedOutputWebhookStatusResponse:
      type: object
      required:
        - success
        - webhook
      properties:
        success:
          type: boolean
          enum:
            - true
        webhook:
          $ref: '#/components/schemas/MappedOutputWebhookStatus'
    MappedOutputWebhookStatus:
      type: object
      required:
        - partnerId
        - enabled
        - signingSecretConfigured
        - etag
      properties:
        partnerId:
          type: integer
        enabled:
          type: boolean
        endpointUrl:
          type: string
          format: uri
          nullable: true
        signingSecretConfigured:
          type: boolean
        signingSecretLastFour:
          type: string
          nullable: true
          description: Last four characters only; the signing secret is never returned.
        activatedAt:
          $ref: '#/components/schemas/NullableUtcTimestamp'
        lastDeliveryAttemptAt:
          $ref: '#/components/schemas/NullableUtcTimestamp'
        lastDeliverySuccessAt:
          $ref: '#/components/schemas/NullableUtcTimestamp'
        lastDeliveryStatusCode:
          type: integer
          nullable: true
        lastDeliveryError:
          type: string
          nullable: true
        createdAt:
          $ref: '#/components/schemas/NullableUtcTimestamp'
        lastUpdatedAt:
          $ref: '#/components/schemas/NullableUtcTimestamp'
        etag:
          type: string
          readOnly: true
          description: Quoted SHA-256 configuration ETag, also returned in the HTTP `ETag` header.
    UsageResponse:
      type: object
      required:
        - success
        - usage
      properties:
        success:
          type: boolean
          enum:
            - true
        usage:
          $ref: '#/components/schemas/UsageSnapshot'
    UsageSnapshot:
      type: object
      required:
        - asOf
        - timeZone
        - attemptedMessagesToday
        - acceptedMessagesToday
        - rejectedMessagesToday
        - rejectionActive
        - unlimited
        - status
        - enforcementMode
        - policy
        - today
        - dailyUsage
        - todayByPartner
        - todayByHour
        - signals
      properties:
        asOf:
          $ref: '#/components/schemas/UtcTimestamp'
        timeZone:
          type: string
          example: UTC
        planCode:
          type: string
          nullable: true
        dailyLimit:
          type: integer
          nullable: true
        warningThreshold:
          type: integer
          nullable: true
        rejectionThreshold:
          type: integer
          nullable: true
        attemptedMessagesToday:
          type: integer
        acceptedMessagesToday:
          type: integer
        rejectedMessagesToday:
          type: integer
        quarantineRetentionDays:
          type: integer
          nullable: true
        rejectionActive:
          type: boolean
        unlimited:
          type: boolean
        status:
          type: string
        enforcementMode:
          type: string
        policy:
          type: string
        today:
          $ref: '#/components/schemas/UsageDay'
        dailyUsage:
          type: array
          items:
            $ref: '#/components/schemas/UsageDay'
        todayByPartner:
          type: array
          items:
            $ref: '#/components/schemas/UsagePartnerBreakdown'
        todayByHour:
          type: array
          items:
            $ref: '#/components/schemas/UsageHour'
        signals:
          $ref: '#/components/schemas/UsageSignals'
    UsageDay:
      type: object
      required:
        - date
        - inboundMessages
        - outboundMessages
        - totalMessages
      properties:
        date:
          type: string
          format: date
        inboundMessages:
          type: integer
        outboundMessages:
          type: integer
        totalMessages:
          type: integer
    UsagePartnerBreakdown:
      type: object
      required:
        - partnerName
        - inboundMessages
        - outboundMessages
        - totalMessages
        - transactionRows
      properties:
        partnerName:
          type: string
        inboundMessages:
          type: integer
        outboundMessages:
          type: integer
        totalMessages:
          type: integer
        transactionRows:
          type: integer
    UsageHour:
      type: object
      required:
        - hour
        - inboundMessages
        - outboundMessages
        - totalMessages
        - transactionRows
      properties:
        hour:
          type: string
          pattern: '^(?:[01][0-9]|2[0-3]):00$'
          description: Hour bucket label in `HH:00` form.
          example: '14:00'
        inboundMessages:
          type: integer
        outboundMessages:
          type: integer
        totalMessages:
          type: integer
        transactionRows:
          type: integer
    UsageSignals:
      type: object
      required:
        - transactionRowsToday
        - multiTransactionMessageIdsToday
        - busiestHourMessages
        - topPartnerMessages
      properties:
        transactionRowsToday:
          type: integer
        multiTransactionMessageIdsToday:
          type: integer
        busiestHour:
          type: string
          nullable: true
          pattern: '^(?:[01][0-9]|2[0-3]):00$'
          example: '18:00'
        busiestHourMessages:
          type: integer
        topPartnerName:
          type: string
          nullable: true
        topPartnerMessages:
          type: integer
    X12ValidationRequest:
      type: object
      required:
        - x12
      properties:
        x12:
          type: string
          description: Complete X12 interchange to parse and validate.
    X12ValidationResponse:
      type: object
      required:
        - success
        - validation
      properties:
        success:
          type: boolean
          enum:
            - true
        validation:
          $ref: '#/components/schemas/X12ValidationResult'
    X12ValidationResult:
      type: object
      required:
        - valid
        - wellFormed
        - errors
        - warnings
        - failures
        - functionalGroupErrors
        - transactionErrors
        - segmentErrors
        - elementErrors
      properties:
        valid:
          type: boolean
        wellFormed:
          type: boolean
        errors:
          type: array
          items:
            type: string
        warnings:
          type: array
          items:
            type: string
        failures:
          type: array
          items:
            type: string
        dispositionError:
          type: string
          nullable: true
        dispositionWarning:
          type: string
          nullable: true
        dispositionFailure:
          type: string
          nullable: true
        functionalGroupErrors:
          type: array
          items:
            type: object
            additionalProperties: true
        transactionErrors:
          type: array
          items:
            type: object
            additionalProperties: true
        segmentErrors:
          type: array
          items:
            type: object
            additionalProperties: true
        elementErrors:
          type: array
          items:
            type: object
            additionalProperties: true
        interchange:
          type: object
          nullable: true
          additionalProperties: true
          description: Parsed interchange summary, including separators, groups, transactions, and previews.
    X12VersionsResponse:
      type: object
      required:
        - success
        - versions
      properties:
        success:
          type: boolean
          enum:
            - true
        versions:
          type: array
          items:
            type: string
          example:
            - '4010'
            - '5010'
    X12TransactionSetsResponse:
      type: object
      required:
        - success
        - x12Version
        - transactionSets
      properties:
        success:
          type: boolean
          enum:
            - true
        x12Version:
          type: string
          example: '4010'
        transactionSets:
          type: array
          items:
            $ref: '#/components/schemas/X12TransactionSetReference'
    X12TransactionSetReference:
      type: object
      required:
        - transactionSetIdentifierCode
        - transactionSetDescription
      properties:
        transactionSetIdentifierCode:
          type: string
          example: '850'
        transactionSetDescription:
          type: string
          example: Purchase Order
    PlatformErrorResponse:
      $ref: '#/components/schemas/ErrorResponse'
    ErrorObject:
      type: object
      required:
        - code
        - message
        - retryable
        - requestId
        - details
      properties:
        code:
          type: string
          example: unauthorized
        message:
          type: string
          example: Missing or invalid integration API key.
        retryable:
          type: boolean
          example: false
        requestId:
          type: string
          example: req_01HZ9TRK7B0YQW2V4RMNC9B60A
        details:
          type: object
          additionalProperties: true
    ErrorResponse:
      type: object
      required:
        - success
        - error
      properties:
        success:
          type: boolean
          enum:
            - false
        error:
          $ref: '#/components/schemas/ErrorObject'
