openapi: 3.0.3
info:
  title: Ingo Duplicate Exchange (Dupex) API
  version: '1'
  description: |
    The Ingo Duplicate Exchange (Dupex) API tells you whether a check has
    been previously presented or accepted anywhere across the Ingo
    network. Dupex is a data service — the client retains the
    underwriting decision. Use Dupex to inform your own check
    decisioning model.

    Three operations form the Dupex workflow:

    1. **Inquiry** — "Has anyone else presented or accepted this check?"
       Returns a `checkId` plus a snapshot of network-wide presentment,
       confirmation, and reversal counts.
    2. **Confirmation** — "I am accepting this check." Records the
       client's acceptance against the `checkId` so subsequent inquiries
       from other network participants see the confirmation.
    3. **Confirmation Reversal** — "I am no longer accepting this check."
       Reverses a prior confirmation by `referenceNumber`.

    **Authentication.** OAuth 2.0 client credentials grant. Production
    token URL: `https://auth.ingo.money/connect/token`. UAT token URL:
    `https://auth-uat.ingo.money/connect/token`. Required scope:
    `https://apis.ingo.money/auth/insights/check/dupex`. Access tokens
    are valid for 3600 seconds — cache and reuse the token across calls.

    **Connectivity.** TLS 1.2 minimum. Client IPs must be on the
    pre-provisioned allowlist. Client credentials are provisioned by Ingo
    Client Services via PGP-encrypted file.

    **Versioning.** Current API version: `1`. Specify via `apiVersion`
    request header or query string. The API defaults to `1` if no
    version is provided.

    **Error handling.** All non-success responses follow RFC 7807
    Problem Details. Record `traceId` on failed requests for support
    escalation.
servers:
  - url: https://dupex-uat.ingo.money
    description: UAT (Sandbox)
  - url: https://dupex.ingo.money
    description: Production
security:
  - OAuth2:
      - https://apis.ingo.money/auth/insights/check/dupex
tags:
  - name: Inquiry
    description: Check whether a check has been previously presented or accepted.
  - name: Confirmation
    description: Record acceptance of a check.
  - name: Confirmation Reversal
    description: Reverse a prior confirmation.
paths:
  /inquiry:
    post:
      tags:
        - Inquiry
      operationId: checkInquiry
      summary: Inquire whether a check has been previously presented
      description: |
        Submit a check for network-wide presentment lookup. Returns the
        check's Dupex identifier (`checkId`) and aggregate counts for
        prior inquiries, confirmations, and confirmation reversals
        across your own client (`self`) and other network participants
        (`other`). Use the returned `checkId` to call `/confirmation`
        once you decide to accept the check.
      parameters:
        - $ref: '#/components/parameters/ApiVersionHeader'
        - $ref: '#/components/parameters/ApiVersionQuery'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InquiryRequest'
            example:
              checkNumber: '1234'
              accountNumber: '6987654321'
              aba: '123456789'
              micr: '56789'
              amountInCents: 5000
              reason: API Testing
              additionalData:
                micrSource: mag-stripe
                checkDate: 07-20-2021
                checkMaker: government
                checkType: stimulus
                transactionReferenceNumber: 567453FT
                customerId: 094857WH
              correlationData:
                - attributeName: AttributeName1
                  attributeValue: AttributeValue1
                - attributeName: AttributeName2
                  attributeValue: AttributeValue2
      responses:
        '200':
          description: Inquiry processed; check status and presentment stats returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InquiryResponse'
              example:
                amountMatches: true
                checkId: 7ab32c19-6a82-4f16-9292-39808caa15d6
                checkStatus: NotUsed
                inquiryStats:
                  self:
                    count: 1
                    mostRecentDateTime: '2021-07-23T19:46:48.7867465Z'
                  other:
                    count: 0
                    mostRecentDateTime: null
                confirmationStats:
                  self:
                    count: 0
                    mostRecentDateTime: null
                  other:
                    count: 0
                    mostRecentDateTime: null
                confirmationReversalStats:
                  self:
                    count: 0
                    mostRecentDateTime: null
                  other:
                    count: 0
                    mostRecentDateTime: null
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /confirmation:
    post:
      tags:
        - Confirmation
      operationId: checkConfirmation
      summary: Record acceptance of a check
      description: |
        Confirm acceptance of a check previously returned by an
        `/inquiry` call. After confirmation, network-wide inquiries by
        other participants will see the confirmation reflected in
        `confirmationStats.other`. Returns a `referenceNumber` to use
        for any subsequent `/confirmation-reversal` call.
      parameters:
        - $ref: '#/components/parameters/ApiVersionHeader'
        - $ref: '#/components/parameters/ApiVersionQuery'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfirmationRequest'
            example:
              checkId: 7ab32c19-6a82-4f16-9292-39808caa15d6
              reason: API Testing
      responses:
        '200':
          description: Confirmation recorded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfirmationResponse'
              example:
                referenceNumber: 16e76453-75d8-49b6-9adb-9b1f18da377a
                checkId: 7ab32c19-6a82-4f16-9292-39808caa15d6
                checkStatus: Used
                inquiryStats:
                  self:
                    count: 1
                    mostRecentDateTime: '2021-07-23T19:46:48.7867465Z'
                  other:
                    count: 0
                    mostRecentDateTime: null
                confirmationStats:
                  self:
                    count: 1
                    mostRecentDateTime: '2021-07-23T19:47:44.1295678Z'
                  other:
                    count: 0
                    mostRecentDateTime: null
                confirmationReversalStats:
                  self:
                    count: 0
                    mostRecentDateTime: null
                  other:
                    count: 0
                    mostRecentDateTime: null
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
  /confirmation-reversal:
    post:
      tags:
        - Confirmation Reversal
      operationId: checkConfirmationReversal
      summary: Reverse a prior confirmation
      description: |
        Reverse a prior `/confirmation` call by its `referenceNumber`.
        Use when you initially accepted a check via `/confirmation` but
        subsequently decided not to. After reversal, the check returns
        to `NotUsed` status (for the same `checkId`).
      parameters:
        - $ref: '#/components/parameters/ApiVersionHeader'
        - $ref: '#/components/parameters/ApiVersionQuery'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfirmationReversalRequest'
            example:
              referenceNumber: 16e76453-75d8-49b6-9adb-9b1f18da377a
              reason: API Testing
      responses:
        '200':
          description: Confirmation reversed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfirmationReversalResponse'
              example:
                checkId: 7ab32c19-6a82-4f16-9292-39808caa15d6
                checkStatus: NotUsed
                inquiryStats:
                  self:
                    count: 1
                    mostRecentDateTime: '2021-07-23T19:46:48.7867465Z'
                  other:
                    count: 0
                    mostRecentDateTime: null
                confirmationStats:
                  self:
                    count: 1
                    mostRecentDateTime: '2021-07-23T19:47:44.1295678Z'
                  other:
                    count: 0
                    mostRecentDateTime: null
                confirmationReversalStats:
                  self:
                    count: 1
                    mostRecentDateTime: '2021-07-23T19:48:42.3804047Z'
                  other:
                    count: 0
                    mostRecentDateTime: null
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
components:
  securitySchemes:
    OAuth2:
      type: oauth2
      description: |
        OAuth 2.0 client credentials grant. Production token URL is
        defined below. For UAT, use
        `https://auth-uat.ingo.money/connect/token` instead. Client
        credentials are provisioned by Ingo Client Services via
        PGP-encrypted file. Tokens are valid for 3600 seconds.
      flows:
        clientCredentials:
          tokenUrl: https://auth.ingo.money/connect/token
          scopes:
            https://apis.ingo.money/auth/insights/check/dupex: Duplicate Exchange check presentment data
  parameters:
    ApiVersionHeader:
      name: apiVersion
      in: header
      required: false
      schema:
        type: string
        default: '1'
      description: |
        API version. Current version is `1`. May also be supplied as a
        query string parameter of the same name. Defaults to `1` if
        omitted.
    ApiVersionQuery:
      name: apiVersion
      in: query
      required: false
      schema:
        type: string
      description: |
        Alternative to the `apiVersion` header. Same semantics. If both
        the header and query parameter are supplied with different
        values, the API returns 400 AmbiguousApiVersion.
  schemas:
    InquiryRequest:
      type: object
      required:
        - checkNumber
        - accountNumber
        - aba
        - micr
        - amountInCents
        - reason
      properties:
        checkNumber:
          type: string
          maxLength: 15
        accountNumber:
          type: string
          maxLength: 25
        aba:
          type: string
          minLength: 9
          maxLength: 9
          description: 9-character ABA routing number.
        micr:
          type: string
          maxLength: 75
        amountInCents:
          type: integer
          minimum: 1
          description: Check amount in cents. Must be greater than zero.
        reason:
          type: string
          maxLength: 150
          description: Free-text reason for the inquiry.
        additionalData:
          $ref: '#/components/schemas/InquiryAdditionalData'
        correlationData:
          type: array
          items:
            $ref: '#/components/schemas/CorrelationAttribute'
          description: Optional client-supplied correlation attributes.
    InquiryAdditionalData:
      type: object
      properties:
        micrSource:
          type: string
          maxLength: 15
          enum:
            - mag-stripe
            - ocr-reader
            - formatted
            - hand-keyed
          description: How the MICR was captured.
        checkDate:
          type: string
          description: |
            Check date in `MM/dd/yyyy` or `MM-dd-yyyy` format.
        checkMaker:
          type: string
          maxLength: 100
        checkType:
          type: string
          maxLength: 50
          enum:
            - cashiers--official
            - govt--recurring
            - govt--other
            - govt--us-treasury
            - stimulus
            - insurance-attorney
            - loan
            - money-order
            - payroll--handwritten
            - payroll--printed
            - rac-check
            - tax-refund--other
            - tax-refund--us-treasury
            - two-party
            - two-party--business
            - other
        transactionReferenceNumber:
          type: string
          maxLength: 50
        customerId:
          type: string
          maxLength: 50
    CorrelationAttribute:
      type: object
      required:
        - attributeName
        - attributeValue
      properties:
        attributeName:
          type: string
          maxLength: 75
        attributeValue:
          type: string
          maxLength: 75
    PresentmentStats:
      type: object
      description: |
        Aggregate counts across `self` (this client) and `other` (all
        other Ingo network participants).
      required:
        - self
        - other
      properties:
        self:
          $ref: '#/components/schemas/PresentmentBucket'
        other:
          $ref: '#/components/schemas/PresentmentBucket'
    PresentmentBucket:
      type: object
      required:
        - count
      properties:
        count:
          type: integer
          minimum: 0
        mostRecentDateTime:
          type: string
          format: date-time
          nullable: true
    InquiryResponse:
      type: object
      required:
        - amountMatches
        - checkId
        - checkStatus
        - inquiryStats
        - confirmationStats
        - confirmationReversalStats
      properties:
        amountMatches:
          type: boolean
          description: |
            Indicates whether the inquiry amount matches the prior
            presentment(s) of this check across the network.
        checkId:
          type: string
          format: uuid
          description: |
            Dupex-assigned identifier for the check. Use this value to
            call `/confirmation`.
        checkStatus:
          type: string
          enum:
            - NotUsed
            - Used
        inquiryStats:
          $ref: '#/components/schemas/PresentmentStats'
        confirmationStats:
          $ref: '#/components/schemas/PresentmentStats'
        confirmationReversalStats:
          $ref: '#/components/schemas/PresentmentStats'
    ConfirmationRequest:
      type: object
      required:
        - checkId
        - reason
      properties:
        checkId:
          type: string
          format: uuid
          description: |
            Dupex `checkId` returned by a prior `/inquiry` response.
        reason:
          type: string
          maxLength: 150
    ConfirmationResponse:
      type: object
      required:
        - referenceNumber
        - checkId
        - checkStatus
        - inquiryStats
        - confirmationStats
        - confirmationReversalStats
      properties:
        referenceNumber:
          type: string
          format: uuid
          description: |
            Dupex-assigned identifier for this confirmation. Use this
            value to call `/confirmation-reversal` if the client later
            decides not to accept the check.
        checkId:
          type: string
          format: uuid
        checkStatus:
          type: string
          enum:
            - NotUsed
            - Used
        inquiryStats:
          $ref: '#/components/schemas/PresentmentStats'
        confirmationStats:
          $ref: '#/components/schemas/PresentmentStats'
        confirmationReversalStats:
          $ref: '#/components/schemas/PresentmentStats'
    ConfirmationReversalRequest:
      type: object
      required:
        - referenceNumber
        - reason
      properties:
        referenceNumber:
          type: string
          format: uuid
          description: |
            Dupex `referenceNumber` returned by a prior `/confirmation`
            response.
        reason:
          type: string
          maxLength: 150
    ConfirmationReversalResponse:
      type: object
      required:
        - checkId
        - checkStatus
        - inquiryStats
        - confirmationStats
        - confirmationReversalStats
      properties:
        checkId:
          type: string
          format: uuid
        checkStatus:
          type: string
          enum:
            - NotUsed
            - Used
        inquiryStats:
          $ref: '#/components/schemas/PresentmentStats'
        confirmationStats:
          $ref: '#/components/schemas/PresentmentStats'
        confirmationReversalStats:
          $ref: '#/components/schemas/PresentmentStats'
    ProblemDetails:
      type: object
      description: |
        RFC 7807 Problem Details. All non-success responses use this
        envelope.
      required:
        - type
        - title
        - status
      properties:
        type:
          type: string
          format: uri
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
        instance:
          type: string
        traceId:
          type: string
          description: |
            Tracing identifier. Record this value on failed requests for
            support escalation.
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
          description: |
            Field-level validation error map (populated on 400
            InputValidationError). Keys are field names; values are
            arrays of error messages.
  responses:
    BadRequest:
      description: |
        Validation, versioning, or other client-side error. Returned for
        input field errors, unsupported API versions, or conflicting
        version specifications.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
          examples:
            inputValidationError:
              summary: Field validation failure
              value:
                type: https://tools.ietf.org/html/rfc7231#section-6.5.1
                title: InputValidationError
                status: 400
                detail: See the errors field for details.
                instance: /inquiry
                traceId: '|bad4a210-4b51729b279dd00f.'
                errors:
                  CheckNumber:
                    - '''Check Number'' must not be empty.'
            unsupportedApiVersion:
              summary: Unsupported API version
              value:
                type: https://tools.ietf.org/html/rfc7231#section-6.5.1
                title: UnsupportedApiVersion
                status: 400
                detail: >-
                  The HTTP resource that matches the request URI does not
                  support the API version '2'.
                traceId: '|bad4a211-4b51729b279dd00f.'
            ambiguousApiVersion:
              summary: Conflicting versions in header and query string
              value:
                type: https://tools.ietf.org/html/rfc7231#section-6.5.1
                title: AmbiguousApiVersion
                status: 400
                detail: >-
                  The following API versions were requested: 1, 2. At most, only
                  a single API version may be specified.
                traceId: '|31f77797-4a8da50f4fd3a851.'
    Unauthorized:
      description: Authorization failure (missing, expired, or invalid bearer token).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
          example:
            type: https://tools.ietf.org/html/rfc7235#section-3.1
            title: Unauthorized
            status: 401
            detail: Authorization failure.
            instance: /inquiry
            traceId: '|b56c1133-4c83cf831979499e.'
    NotFound:
      description: Request to a resource URL that does not exist on this service.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
          example:
            type: https://tools.ietf.org/html/rfc7231#section-6.5.4
            title: ResourceNotFound
            status: 404
            detail: Resource '/resource-not-located' not found.
            instance: /resource-not-located
            traceId: '|ddcf5087-4b680597207c67c5.'
    InternalServerError:
      description: >-
        Unexpected server-side error. Contact Ingo Client Services with the
        `traceId`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
          example:
            type: https://tools.ietf.org/html/rfc7231#section-6.6.1
            title: InternalServerError.
            status: 500
            detail: Please contact Ingo money client services.
            instance: /inquiry
            traceId: '|c1ecee92-4f4cec828884a904.'
            errors: {}
