openapi: 3.1.0
info:
  title: LendEasy Servicing Connector API
  version: 1.0.0
  summary: Closed connector callbacks implemented by the composed servicing runtime.
  description: The three externally reachable connector callbacks. These routes use a pinned gateway signature or
    dispatch-bound worker token and are intentionally excluded from the ordinary client contract.
  contact:
    name: LendEasy Developer Experience
    url: https://docs.lendeasy.ai
  x-operation-count: 7
servers:
  - url: https://sandbox.api.lendeasy.ai
    description: Sandbox with simulated money rails.
  - url: https://api.lendeasy.ai
    description: Production.
tags:
  - name: Connector callbacks
    x-surface: servicing
    description: Gateway-signed channel events and dispatch-bound voice callbacks.
  - name: Portal contracts
    x-surface: servicing
    description: "The servicing side of borrower self-service: agreements, documents, invitations, interactions, message
      tasks, signals, and upload sessions."
security: []
paths:
  /v1/servicing/channel-events/{gatewayCode}:
    post:
      tags:
        - Connector callbacks
      operationId: ingestChannelGatewayEvent
      summary: Ingest a channel gateway event
      description: Verifies a pinned gateway signature over the exact JSON bytes and normalizes the configured provider event.
        The example is the MANUAL adapter’s normalized body; live adapters retain their pinned gateway-specific shape.
      x-idempotent: false
      x-availability: implemented
      x-audience: connector
      x-error-contract: connector-json
      security:
        - gatewaySignature: []
      parameters:
        - name: gatewayCode
          in: path
          required: true
          description: Stable opaque gatewayCode identifier.
          schema:
            type: string
      responses:
        "200":
          description: Successful response.
          content:
            application/json:
              schema:
                type: object
                required:
                  - eventId
                  - duplicate
                  - disposition
                properties:
                  eventId:
                    type: integer
                  duplicate:
                    type: boolean
                  disposition:
                    type: string
              example:
                eventId: 9901
                duplicate: false
                disposition: UPDATED
        "400":
          $ref: "#/components/responses/Connector400Error"
        "401":
          $ref: "#/components/responses/Connector401Error"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
              description: Pinned gateway-specific JSON payload. The MANUAL adapter accepts the normalized example shown here.
            example:
              gatewayEventRef: gateway-event-991
              eventType: CUSTOMER_SMS_OPT_OUT
              channel: SMS
              occurredAt: 2026-07-25T18:30:00Z
              relatedObjectType: MESSAGE
              relatedObjectRef: message-71
              normalizedStatus: OPTED_OUT
      x-connector-authority: Gateway signature or service authentication
      x-connector-header: X-LendEasy-Gateway-Signature
  /v1/servicing/voice-dispatches/{dispatchId}/dial-authorization:
    post:
      tags:
        - Connector callbacks
      operationId: authorizeVoiceDial
      summary: Authorize a voice dial
      description: Rechecks the dispatch-bound target at execution time. This connector callback uses a task-scoped worker
        token and accepts no body.
      x-idempotent: false
      x-availability: implemented
      x-audience: connector
      x-error-contract: connector-json
      security:
        - voiceTaskToken: []
      parameters:
        - name: dispatchId
          in: path
          required: true
          description: Positive numeric dispatch identifier.
          schema:
            type: integer
            format: int64
            minimum: 1
      responses:
        "200":
          description: Successful response.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required:
                  - dispatchId
                  - contactAttemptId
                  - decisionRef
                  - outcome
                  - dialAuthorized
                properties:
                  dispatchId:
                    type: integer
                    format: int64
                    minimum: 1
                  contactAttemptId:
                    type: integer
                    format: int64
                    minimum: 1
                  decisionRef:
                    type: string
                  outcome:
                    type: string
                  dialAuthorized:
                    type: boolean
                  expiresAt:
                    type: string
                    format: date-time
                    description: Present only when dialAuthorized is true.
              example:
                dispatchId: 731
                contactAttemptId: 8801
                decisionRef: decision-441
                outcome: ALLOWED
                dialAuthorized: true
                expiresAt: 2026-07-25T19:05:30-07:00
        "400":
          $ref: "#/components/responses/Connector400Error"
        "401":
          $ref: "#/components/responses/Connector401Error"
        "404":
          $ref: "#/components/responses/Connector404Error"
        "409":
          $ref: "#/components/responses/Connector409Error"
      x-connector-authority: Dispatch-bound task-scoped worker token
      x-connector-header: X-LendEasy-Voice-Task-Token
  /v1/servicing/voice-dispatches/{dispatchId}/disposition:
    post:
      tags:
        - Connector callbacks
      operationId: submitVoiceDisposition
      summary: Submit a voice disposition
      description: Validates one closed primary/secondary outcome set and applies its interaction, case, task, consent,
        promise, payment, and handoff effects idempotently.
      x-idempotent: false
      x-availability: implemented
      x-audience: connector
      x-error-contract: connector-json
      security:
        - voiceTaskToken: []
      parameters:
        - name: dispatchId
          in: path
          required: true
          description: Positive numeric dispatch identifier.
          schema:
            type: integer
            format: int64
            minimum: 1
      responses:
        "200":
          description: Successful response.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required:
                  - dispatchId
                  - agentDispositionRef
                  - accepted
                  - interactionId
                  - outcomeCode
                  - secondaryOutcomeCodes
                  - effects
                properties:
                  dispatchId:
                    type: integer
                    format: int64
                    minimum: 1
                  agentDispositionRef:
                    type: string
                  accepted:
                    type: boolean
                    const: true
                  interactionId:
                    type: integer
                    format: int64
                    minimum: 1
                  outcomeCode:
                    type: string
                    enum:
                      - PAYMENT_CAPTURED
                      - PARTIAL_PAYMENT_CAPTURED
                      - PAY_AHEAD_PAYMENT_CAPTURED
                      - LOAN_PAYOFF_CAPTURED
                      - SCHEDULED_PAYMENT_CAPTURED
                      - LATE_FEE_WAIVED_AND_PAYMENT_CAPTURED
                      - LATE_FEE_WAIVED_AND_SCHEDULED_PAYMENT_CAPTURED
                      - PROMISE_TO_PAY_RECORDED
                      - PAYMENT_LINK_SENT
                      - PAYMENT_LINK_SENT_SAME_DAY
                      - PAYOFF_LINK_SENT
                      - PAYOFF_INFO_PROVIDED
                      - EXISTING_SCHEDULED_PAYMENT_NOTED
                      - BORROWER_WILL_PAY_THROUGH_PORTAL_TODAY
                      - AGENT_CALLBACK_SCHEDULED
                      - AGENT_CALLBACK_SCHEDULED_AFTER_DISCONNECT
                      - AGENT_CALLBACK_SCHEDULED_WITH_OFF_HOURS_CONSENT
                      - HUMAN_CALLBACK_SCHEDULED
                      - LANGUAGE_SPECIALIST_CALLBACK_REQUESTED
                      - BORROWER_WILL_CALL_BACK
                      - DISPUTE_CAPTURED
                      - DISPUTE_PENDING_REVIEW
                      - DEBT_DENIAL_ESCALATED
                      - TERMS_DISPUTE_ESCALATED
                      - PRIOR_REP_PROMISE_ESCALATED
                      - PRIOR_REP_PROMISE_REVIEW_DECLINED
                      - HARDSHIP_AFFORDABILITY_REVIEW_REQUESTED
                      - HARDSHIP_CALLBACK_REQUESTED
                      - HARDSHIP_OPTION_REVIEW_REQUESTED
                      - HARDSHIP_SETTLEMENT_REVIEW_REQUESTED
                      - HARDSHIP_PARTIAL_PAYMENT_AND_CALLBACK
                      - HARDSHIP_FULL_CURE_SCHEDULED
                      - HARDSHIP_SPLIT_CURE_SCHEDULED
                      - HARDSHIP_ONE_PAYMENT_DEFERRAL_APPROVED
                      - HARDSHIP_DISASTER_RELIEF_REVIEW_REQUESTED
                      - DO_NOT_CALL
                      - WRITTEN_ONLY_CONTACT_REQUESTED
                      - AI_CALL_CONSENT_REVOKED
                      - RECORDING_CONSENT_REVOKED
                      - BANKRUPTCY_OR_ATTORNEY_NOTED_CALL_ENDED
                      - MILITARY_OR_SCRA_NOTED
                      - MILITARY_OR_SCRA_SPECIALIST_CALLBACK_REQUESTED
                      - DECEASED_BORROWER_NOTED
                      - SELF_HARM_OR_DISTRESS_ESCALATED
                      - REGULATORY_COMPLAINT_ESCALATED
                      - NO_ANSWER
                      - VOICEMAIL_LEFT
                      - WRONG_PARTY
                      - BORROWER_UNAVAILABLE_NO_DETAILS_DISCLOSED
                      - CALL_DROPPED_BEFORE_VERIFICATION
                      - VERIFICATION_FAILED
                      - VERIFICATION_REFUSED
                      - THIRD_PARTY_MANAGER_NOTED
                      - CALL_TERMINATED_HOSTILE
                      - NO_RESOLUTION
                      - NO_RESOLUTION_LOW_CONFIDENCE
                      - PAYMENT_FAILED_FOLLOW_UP
                      - PAYMENT_REVERSAL_REQUESTED
                  secondaryOutcomeCodes:
                    type: array
                    items:
                      type: string
                      enum:
                        - PAYMENT_CAPTURED
                        - PARTIAL_PAYMENT_CAPTURED
                        - PAY_AHEAD_PAYMENT_CAPTURED
                        - LOAN_PAYOFF_CAPTURED
                        - SCHEDULED_PAYMENT_CAPTURED
                        - LATE_FEE_WAIVED_AND_PAYMENT_CAPTURED
                        - LATE_FEE_WAIVED_AND_SCHEDULED_PAYMENT_CAPTURED
                        - PROMISE_TO_PAY_RECORDED
                        - PAYMENT_LINK_SENT
                        - PAYMENT_LINK_SENT_SAME_DAY
                        - PAYOFF_LINK_SENT
                        - PAYOFF_INFO_PROVIDED
                        - EXISTING_SCHEDULED_PAYMENT_NOTED
                        - BORROWER_WILL_PAY_THROUGH_PORTAL_TODAY
                        - AGENT_CALLBACK_SCHEDULED
                        - AGENT_CALLBACK_SCHEDULED_AFTER_DISCONNECT
                        - AGENT_CALLBACK_SCHEDULED_WITH_OFF_HOURS_CONSENT
                        - HUMAN_CALLBACK_SCHEDULED
                        - LANGUAGE_SPECIALIST_CALLBACK_REQUESTED
                        - BORROWER_WILL_CALL_BACK
                        - DISPUTE_CAPTURED
                        - DISPUTE_PENDING_REVIEW
                        - DEBT_DENIAL_ESCALATED
                        - TERMS_DISPUTE_ESCALATED
                        - PRIOR_REP_PROMISE_ESCALATED
                        - PRIOR_REP_PROMISE_REVIEW_DECLINED
                        - HARDSHIP_AFFORDABILITY_REVIEW_REQUESTED
                        - HARDSHIP_CALLBACK_REQUESTED
                        - HARDSHIP_OPTION_REVIEW_REQUESTED
                        - HARDSHIP_SETTLEMENT_REVIEW_REQUESTED
                        - HARDSHIP_PARTIAL_PAYMENT_AND_CALLBACK
                        - HARDSHIP_FULL_CURE_SCHEDULED
                        - HARDSHIP_SPLIT_CURE_SCHEDULED
                        - HARDSHIP_ONE_PAYMENT_DEFERRAL_APPROVED
                        - HARDSHIP_DISASTER_RELIEF_REVIEW_REQUESTED
                        - DO_NOT_CALL
                        - WRITTEN_ONLY_CONTACT_REQUESTED
                        - AI_CALL_CONSENT_REVOKED
                        - RECORDING_CONSENT_REVOKED
                        - BANKRUPTCY_OR_ATTORNEY_NOTED_CALL_ENDED
                        - MILITARY_OR_SCRA_NOTED
                        - MILITARY_OR_SCRA_SPECIALIST_CALLBACK_REQUESTED
                        - DECEASED_BORROWER_NOTED
                        - SELF_HARM_OR_DISTRESS_ESCALATED
                        - REGULATORY_COMPLAINT_ESCALATED
                        - NO_ANSWER
                        - VOICEMAIL_LEFT
                        - WRONG_PARTY
                        - BORROWER_UNAVAILABLE_NO_DETAILS_DISCLOSED
                        - CALL_DROPPED_BEFORE_VERIFICATION
                        - VERIFICATION_FAILED
                        - VERIFICATION_REFUSED
                        - THIRD_PARTY_MANAGER_NOTED
                        - CALL_TERMINATED_HOSTILE
                        - NO_RESOLUTION
                        - NO_RESOLUTION_LOW_CONFIDENCE
                        - PAYMENT_FAILED_FOLLOW_UP
                        - PAYMENT_REVERSAL_REQUESTED
                  effects:
                    type: array
                    items:
                      type: object
                      additionalProperties: false
                      required:
                        - type
                        - target
                        - status
                      properties:
                        type:
                          type: string
                        target:
                          type: string
                        status:
                          type: string
                        ref:
                          type: string
              example:
                dispatchId: 731
                agentDispositionRef: voice-result-731
                accepted: true
                interactionId: 3004
                outcomeCode: PROMISE_TO_PAY_RECORDED
                secondaryOutcomeCodes: []
                effects:
                  - type: PROMISE
                    target: case:1001
                    status: COMPLETED
                    ref: "6101"
        "400":
          $ref: "#/components/responses/Connector400Error"
        "401":
          $ref: "#/components/responses/Connector401Error"
        "404":
          $ref: "#/components/responses/Connector404Error"
        "409":
          $ref: "#/components/responses/Connector409Error"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required:
                - agentDispositionRef
                - outcomeCode
                - occurredAt
                - verificationState
                - runtimeProvenance
              properties:
                agentDispositionRef:
                  type: string
                  minLength: 1
                  maxLength: 255
                outcomeCode:
                  type: string
                  enum:
                    - PAYMENT_CAPTURED
                    - PARTIAL_PAYMENT_CAPTURED
                    - PAY_AHEAD_PAYMENT_CAPTURED
                    - LOAN_PAYOFF_CAPTURED
                    - SCHEDULED_PAYMENT_CAPTURED
                    - LATE_FEE_WAIVED_AND_PAYMENT_CAPTURED
                    - LATE_FEE_WAIVED_AND_SCHEDULED_PAYMENT_CAPTURED
                    - PROMISE_TO_PAY_RECORDED
                    - PAYMENT_LINK_SENT
                    - PAYMENT_LINK_SENT_SAME_DAY
                    - PAYOFF_LINK_SENT
                    - PAYOFF_INFO_PROVIDED
                    - EXISTING_SCHEDULED_PAYMENT_NOTED
                    - BORROWER_WILL_PAY_THROUGH_PORTAL_TODAY
                    - AGENT_CALLBACK_SCHEDULED
                    - AGENT_CALLBACK_SCHEDULED_AFTER_DISCONNECT
                    - AGENT_CALLBACK_SCHEDULED_WITH_OFF_HOURS_CONSENT
                    - HUMAN_CALLBACK_SCHEDULED
                    - LANGUAGE_SPECIALIST_CALLBACK_REQUESTED
                    - BORROWER_WILL_CALL_BACK
                    - DISPUTE_CAPTURED
                    - DISPUTE_PENDING_REVIEW
                    - DEBT_DENIAL_ESCALATED
                    - TERMS_DISPUTE_ESCALATED
                    - PRIOR_REP_PROMISE_ESCALATED
                    - PRIOR_REP_PROMISE_REVIEW_DECLINED
                    - HARDSHIP_AFFORDABILITY_REVIEW_REQUESTED
                    - HARDSHIP_CALLBACK_REQUESTED
                    - HARDSHIP_OPTION_REVIEW_REQUESTED
                    - HARDSHIP_SETTLEMENT_REVIEW_REQUESTED
                    - HARDSHIP_PARTIAL_PAYMENT_AND_CALLBACK
                    - HARDSHIP_FULL_CURE_SCHEDULED
                    - HARDSHIP_SPLIT_CURE_SCHEDULED
                    - HARDSHIP_ONE_PAYMENT_DEFERRAL_APPROVED
                    - HARDSHIP_DISASTER_RELIEF_REVIEW_REQUESTED
                    - DO_NOT_CALL
                    - WRITTEN_ONLY_CONTACT_REQUESTED
                    - AI_CALL_CONSENT_REVOKED
                    - RECORDING_CONSENT_REVOKED
                    - BANKRUPTCY_OR_ATTORNEY_NOTED_CALL_ENDED
                    - MILITARY_OR_SCRA_NOTED
                    - MILITARY_OR_SCRA_SPECIALIST_CALLBACK_REQUESTED
                    - DECEASED_BORROWER_NOTED
                    - SELF_HARM_OR_DISTRESS_ESCALATED
                    - REGULATORY_COMPLAINT_ESCALATED
                    - NO_ANSWER
                    - VOICEMAIL_LEFT
                    - WRONG_PARTY
                    - BORROWER_UNAVAILABLE_NO_DETAILS_DISCLOSED
                    - CALL_DROPPED_BEFORE_VERIFICATION
                    - VERIFICATION_FAILED
                    - VERIFICATION_REFUSED
                    - THIRD_PARTY_MANAGER_NOTED
                    - CALL_TERMINATED_HOSTILE
                    - NO_RESOLUTION
                    - NO_RESOLUTION_LOW_CONFIDENCE
                    - PAYMENT_FAILED_FOLLOW_UP
                    - PAYMENT_REVERSAL_REQUESTED
                  description: One exact primary code from the closed 58-code registry.
                secondaryOutcomeCodes:
                  type: array
                  maxItems: 8
                  uniqueItems: true
                  items:
                    type: string
                    enum:
                      - PAYMENT_CAPTURED
                      - PARTIAL_PAYMENT_CAPTURED
                      - PAY_AHEAD_PAYMENT_CAPTURED
                      - LOAN_PAYOFF_CAPTURED
                      - SCHEDULED_PAYMENT_CAPTURED
                      - LATE_FEE_WAIVED_AND_PAYMENT_CAPTURED
                      - LATE_FEE_WAIVED_AND_SCHEDULED_PAYMENT_CAPTURED
                      - PROMISE_TO_PAY_RECORDED
                      - PAYMENT_LINK_SENT
                      - PAYMENT_LINK_SENT_SAME_DAY
                      - PAYOFF_LINK_SENT
                      - PAYOFF_INFO_PROVIDED
                      - EXISTING_SCHEDULED_PAYMENT_NOTED
                      - BORROWER_WILL_PAY_THROUGH_PORTAL_TODAY
                      - AGENT_CALLBACK_SCHEDULED
                      - AGENT_CALLBACK_SCHEDULED_AFTER_DISCONNECT
                      - AGENT_CALLBACK_SCHEDULED_WITH_OFF_HOURS_CONSENT
                      - HUMAN_CALLBACK_SCHEDULED
                      - LANGUAGE_SPECIALIST_CALLBACK_REQUESTED
                      - BORROWER_WILL_CALL_BACK
                      - DISPUTE_CAPTURED
                      - DISPUTE_PENDING_REVIEW
                      - DEBT_DENIAL_ESCALATED
                      - TERMS_DISPUTE_ESCALATED
                      - PRIOR_REP_PROMISE_ESCALATED
                      - PRIOR_REP_PROMISE_REVIEW_DECLINED
                      - HARDSHIP_AFFORDABILITY_REVIEW_REQUESTED
                      - HARDSHIP_CALLBACK_REQUESTED
                      - HARDSHIP_OPTION_REVIEW_REQUESTED
                      - HARDSHIP_SETTLEMENT_REVIEW_REQUESTED
                      - HARDSHIP_PARTIAL_PAYMENT_AND_CALLBACK
                      - HARDSHIP_FULL_CURE_SCHEDULED
                      - HARDSHIP_SPLIT_CURE_SCHEDULED
                      - HARDSHIP_ONE_PAYMENT_DEFERRAL_APPROVED
                      - HARDSHIP_DISASTER_RELIEF_REVIEW_REQUESTED
                      - DO_NOT_CALL
                      - WRITTEN_ONLY_CONTACT_REQUESTED
                      - AI_CALL_CONSENT_REVOKED
                      - RECORDING_CONSENT_REVOKED
                      - BANKRUPTCY_OR_ATTORNEY_NOTED_CALL_ENDED
                      - MILITARY_OR_SCRA_NOTED
                      - MILITARY_OR_SCRA_SPECIALIST_CALLBACK_REQUESTED
                      - DECEASED_BORROWER_NOTED
                      - SELF_HARM_OR_DISTRESS_ESCALATED
                      - REGULATORY_COMPLAINT_ESCALATED
                      - NO_ANSWER
                      - VOICEMAIL_LEFT
                      - WRONG_PARTY
                      - BORROWER_UNAVAILABLE_NO_DETAILS_DISCLOSED
                      - CALL_DROPPED_BEFORE_VERIFICATION
                      - VERIFICATION_FAILED
                      - VERIFICATION_REFUSED
                      - THIRD_PARTY_MANAGER_NOTED
                      - CALL_TERMINATED_HOSTILE
                      - NO_RESOLUTION
                      - NO_RESOLUTION_LOW_CONFIDENCE
                      - PAYMENT_FAILED_FOLLOW_UP
                      - PAYMENT_REVERSAL_REQUESTED
                  description: Compatible exact secondary codes; none may equal outcomeCode.
                occurredAt:
                  type: string
                  format: date-time
                verificationState:
                  type: string
                  enum:
                    - UNVERIFIED
                    - PARTIALLY_VERIFIED
                    - VERIFIED
                    - NOT_APPLICABLE
                confidence:
                  type: number
                  minimum: 0
                  maximum: 1
                runtimeProvenance:
                  type: object
                  additionalProperties: false
                  required:
                    - runtimeVersion
                  properties:
                    runtimeVersion:
                      type: string
                      minLength: 1
                      maxLength: 255
                    evidenceRef:
                      type: string
                      minLength: 1
                      maxLength: 255
                transcriptRef:
                  type: string
                  minLength: 1
                  maxLength: 255
                payments:
                  type: array
                  minItems: 1
                  maxItems: 2
                  items:
                    type: object
                    additionalProperties: false
                    required:
                      - outcomeCode
                      - amount
                      - valueDate
                      - authorizationRef
                    properties:
                      outcomeCode:
                        type: string
                        enum:
                          - PAYMENT_CAPTURED
                          - PARTIAL_PAYMENT_CAPTURED
                          - PAY_AHEAD_PAYMENT_CAPTURED
                          - LOAN_PAYOFF_CAPTURED
                          - SCHEDULED_PAYMENT_CAPTURED
                          - LATE_FEE_WAIVED_AND_PAYMENT_CAPTURED
                          - LATE_FEE_WAIVED_AND_SCHEDULED_PAYMENT_CAPTURED
                          - PROMISE_TO_PAY_RECORDED
                          - PAYMENT_LINK_SENT
                          - PAYMENT_LINK_SENT_SAME_DAY
                          - PAYOFF_LINK_SENT
                          - PAYOFF_INFO_PROVIDED
                          - EXISTING_SCHEDULED_PAYMENT_NOTED
                          - BORROWER_WILL_PAY_THROUGH_PORTAL_TODAY
                          - AGENT_CALLBACK_SCHEDULED
                          - AGENT_CALLBACK_SCHEDULED_AFTER_DISCONNECT
                          - AGENT_CALLBACK_SCHEDULED_WITH_OFF_HOURS_CONSENT
                          - HUMAN_CALLBACK_SCHEDULED
                          - LANGUAGE_SPECIALIST_CALLBACK_REQUESTED
                          - BORROWER_WILL_CALL_BACK
                          - DISPUTE_CAPTURED
                          - DISPUTE_PENDING_REVIEW
                          - DEBT_DENIAL_ESCALATED
                          - TERMS_DISPUTE_ESCALATED
                          - PRIOR_REP_PROMISE_ESCALATED
                          - PRIOR_REP_PROMISE_REVIEW_DECLINED
                          - HARDSHIP_AFFORDABILITY_REVIEW_REQUESTED
                          - HARDSHIP_CALLBACK_REQUESTED
                          - HARDSHIP_OPTION_REVIEW_REQUESTED
                          - HARDSHIP_SETTLEMENT_REVIEW_REQUESTED
                          - HARDSHIP_PARTIAL_PAYMENT_AND_CALLBACK
                          - HARDSHIP_FULL_CURE_SCHEDULED
                          - HARDSHIP_SPLIT_CURE_SCHEDULED
                          - HARDSHIP_ONE_PAYMENT_DEFERRAL_APPROVED
                          - HARDSHIP_DISASTER_RELIEF_REVIEW_REQUESTED
                          - DO_NOT_CALL
                          - WRITTEN_ONLY_CONTACT_REQUESTED
                          - AI_CALL_CONSENT_REVOKED
                          - RECORDING_CONSENT_REVOKED
                          - BANKRUPTCY_OR_ATTORNEY_NOTED_CALL_ENDED
                          - MILITARY_OR_SCRA_NOTED
                          - MILITARY_OR_SCRA_SPECIALIST_CALLBACK_REQUESTED
                          - DECEASED_BORROWER_NOTED
                          - SELF_HARM_OR_DISTRESS_ESCALATED
                          - REGULATORY_COMPLAINT_ESCALATED
                          - NO_ANSWER
                          - VOICEMAIL_LEFT
                          - WRONG_PARTY
                          - BORROWER_UNAVAILABLE_NO_DETAILS_DISCLOSED
                          - CALL_DROPPED_BEFORE_VERIFICATION
                          - VERIFICATION_FAILED
                          - VERIFICATION_REFUSED
                          - THIRD_PARTY_MANAGER_NOTED
                          - CALL_TERMINATED_HOSTILE
                          - NO_RESOLUTION
                          - NO_RESOLUTION_LOW_CONFIDENCE
                          - PAYMENT_FAILED_FOLLOW_UP
                          - PAYMENT_REVERSAL_REQUESTED
                      instrumentId:
                        type: string
                      amount:
                        type: object
                        additionalProperties: false
                        required:
                          - amount
                          - currency
                        properties:
                          amount:
                            oneOf:
                              - type: number
                              - type: string
                                pattern: ^-?[0-9]+(?:\.[0-9]+)?$
                            description: Decimal amount accepted as a JSON number or decimal string.
                          currency:
                            type: string
                            pattern: ^[A-Z]{3}$
                      valueDate:
                        type: string
                        format: date
                      authorizationRef:
                        type: string
                        minLength: 1
                  description: Required only for a submitted payment-bearing outcome; authorizationRef values must be unique.
                reversal:
                  type: object
                  description: Required only for PAYMENT_REVERSAL_REQUESTED.
                  properties:
                    paymentRef:
                      type: string
                promise:
                  type: object
                  additionalProperties: false
                  required:
                    - planType
                    - reason
                  properties:
                    planType:
                      type: string
                      enum:
                        - SINGLE
                        - INSTALLMENT
                    promiseDate:
                      type: string
                      format: date
                    promiseAmount:
                      type: object
                      additionalProperties: false
                      required:
                        - amount
                        - currency
                      properties:
                        amount:
                          oneOf:
                            - type: number
                            - type: string
                              pattern: ^-?[0-9]+(?:\.[0-9]+)?$
                          description: Decimal amount accepted as a JSON number or decimal string.
                        currency:
                          type: string
                          pattern: ^[A-Z]{3}$
                    installments:
                      type: array
                      minItems: 1
                      items:
                        type: object
                        additionalProperties: false
                        required:
                          - sequence
                          - dueDate
                          - amount
                        properties:
                          sequence:
                            type: integer
                            minimum: 1
                          dueDate:
                            type: string
                            format: date
                          amount:
                            type: object
                            additionalProperties: false
                            required:
                              - amount
                              - currency
                            properties:
                              amount:
                                oneOf:
                                  - type: number
                                  - type: string
                                    pattern: ^-?[0-9]+(?:\.[0-9]+)?$
                                description: Decimal amount accepted as a JSON number or decimal string.
                              currency:
                                type: string
                                pattern: ^[A-Z]{3}$
                    schedule:
                      type: object
                    reason:
                      type: string
                      minLength: 1
                      maxLength: 2000
                    evidenceIds:
                      type: array
                      items:
                        type: integer
                        format: int64
                        minimum: 1
                  oneOf:
                    - properties:
                        planType:
                          const: SINGLE
                      required:
                        - promiseDate
                        - promiseAmount
                    - properties:
                        planType:
                          const: INSTALLMENT
                      required:
                        - installments
                  description: Required only for PROMISE_TO_PAY_RECORDED.
                callback:
                  type: object
                  properties:
                    scheduledAt:
                      type: string
                      format: date-time
                  description: Required only for a submitted callback outcome.
                consentChanges:
                  type: array
                  items:
                    type: object
                    additionalProperties: false
                    required:
                      - consentType
                      - operation
                    properties:
                      consentType:
                        type: string
                        enum:
                          - ARTIFICIAL_VOICE
                          - UNUSUAL_TIME
                      operation:
                        type: string
                        enum:
                          - REVOKE
                          - GRANT
                      effectiveAt:
                        type: string
                        format: date-time
                      evidenceRef:
                        type: string
                  description: Required only when the submitted outcome fixes a consent change.
                recordingState:
                  type: string
                  enum:
                    - NOT_RECORDED
                    - POLICY_PENDING
                    - RECORDED
                    - STOPPED
                    - BLOCKED
                    - UNAVAILABLE
                  description: Required for a connected interaction and rejected for an unconnected one.
                escalationFlags:
                  type: array
                  uniqueItems: true
                  items:
                    type: string
                    enum:
                      - IDENTITY_THEFT_SUSPECTED
                      - UNVERIFIED_DISPUTE
                      - REGISTERED_EMAIL_ISSUE
                      - LONG_TERM_MODIFICATION_REVIEW
                      - FINANCIAL_EXHAUSTION
                handoffSummary:
                  type: string
                  minLength: 1
                  maxLength: 2000
                  description: Required only when the submitted outcome creates human work.
            example:
              agentDispositionRef: voice-result-731
              outcomeCode: PROMISE_TO_PAY_RECORDED
              secondaryOutcomeCodes: []
              occurredAt: 2026-07-25T19:12:31-07:00
              verificationState: VERIFIED
              runtimeProvenance:
                runtimeVersion: collections-voice-2026.07.25
                evidenceRef: worker-eval-731
              transcriptRef: call-transcript-731
              recordingState: RECORDED
              promise:
                planType: SINGLE
                promiseDate: 2026-08-01
                promiseAmount:
                  amount: 250
                  currency: USD
                reason: Customer committed during the verified call.
      x-connector-authority: Dispatch-bound task-scoped worker token
      x-connector-header: X-LendEasy-Voice-Task-Token
  /v1/servicing/borrower-document-uploads/exchange:
    post:
      tags:
        - Portal contracts
      operationId: exchangeBorrowerUploadToken
      summary: Exchange an upload link for a session
      description: Exchanges the single-purpose emailed link secret for a short-lived session bound to one case, one customer,
        and one evidence purpose, returning borrower-safe upload instructions. Refusals are a flat 200 with a closed
        reason so a probing caller learns nothing about which guess was closer.
      x-idempotent: false
      x-availability: implemented
      x-audience: connector
      x-error-contract: connector-json
      security:
        - uploadLinkToken: []
      parameters: []
      responses:
        "200":
          description: Successful response.
          content:
            application/json:
              schema:
                type: object
                required:
                  - accepted
                  - sessionToken
                  - expiresAt
                  - instructions
                properties:
                  accepted:
                    type: boolean
                  sessionToken:
                    type: string
                  expiresAt:
                    type: string
                    format: date-time
                  instructions:
                    type: object
                    required:
                      - evidenceType
                      - state
                      - expiresAt
                      - uploadsRemaining
                      - title
                      - mustShow
                      - doNotSend
                      - privacyNote
                      - acceptedFormats
                    properties:
                      evidenceType:
                        type: string
                      state:
                        type: string
                      expiresAt:
                        type: string
                        format: date-time
                      uploadsRemaining:
                        type: integer
                      title:
                        type: string
                      mustShow:
                        type: array
                        items:
                          type: string
                      doNotSend:
                        type: array
                        items:
                          type: string
                      privacyNote:
                        type: string
                      acceptedFormats:
                        type: array
                        items:
                          type: string
              example:
                accepted: true
                sessionToken: <opaque short-lived session token>
                expiresAt: 2026-07-25T19:10:00Z
                instructions:
                  evidenceType: DOCTORS_MEDICAL_LEAVE_CERTIFICATE
                  state: OPENED
                  expiresAt: 2026-08-01T18:00:00Z
                  uploadsRemaining: 1
                  title: Please send us your doctor's letter
                  mustShow:
                    - Your name
                    - That you are currently unable to work
                    - The date your leave started
                    - The date you are expected to return to work
                    - Your doctor's name, practice and signature
                    - The date they issued it
                  doNotSend:
                    - A diagnosis, symptoms, treatment or medication
                    - Your medical history or records
                    - An employer leave letter
                    - Disability benefit paperwork
                  privacyNote: We only need the facts listed above. Please do not send anything else — we do not need it and we would
                    rather not hold it.
                  acceptedFormats:
                    - PDF
                    - JPEG
                    - PNG
        "400":
          $ref: "#/components/responses/Connector400Error"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - linkToken
              properties:
                linkToken:
                  type: string
              example:
                linkToken: <link secret from the emailed URL fragment>
            example:
              linkToken: <link secret from the emailed URL fragment>
      x-connector-authority: Single-use emailed link secret carried in the request body
  /v1/servicing/borrower-document-uploads/current:
    get:
      tags:
        - Portal contracts
      operationId: getBorrowerUploadInstructions
      summary: Retrieve the current upload instructions
      description: Returns what to send, by when, and how many uploads remain for the session. The payload carries only the
        lender's own copy and the invitation state — no borrower name, account number, loan detail, or internal
        identifier.
      x-idempotent: false
      x-availability: implemented
      x-audience: connector
      x-error-contract: connector-json
      security:
        - uploadSession: []
      parameters: []
      responses:
        "200":
          description: Successful response.
          content:
            application/json:
              schema:
                type: object
                required:
                  - accepted
                  - instructions
                properties:
                  accepted:
                    type: boolean
                  instructions:
                    type: object
                    required:
                      - evidenceType
                      - state
                      - expiresAt
                      - uploadsRemaining
                      - title
                      - mustShow
                      - doNotSend
                      - privacyNote
                      - acceptedFormats
                    properties:
                      evidenceType:
                        type: string
                      state:
                        type: string
                      expiresAt:
                        type: string
                        format: date-time
                      uploadsRemaining:
                        type: integer
                      title:
                        type: string
                      mustShow:
                        type: array
                        items:
                          type: string
                      doNotSend:
                        type: array
                        items:
                          type: string
                      privacyNote:
                        type: string
                      acceptedFormats:
                        type: array
                        items:
                          type: string
              example:
                accepted: true
                instructions:
                  evidenceType: DOCTORS_MEDICAL_LEAVE_CERTIFICATE
                  state: OPENED
                  expiresAt: 2026-08-01T18:00:00Z
                  uploadsRemaining: 1
                  title: Please send us your doctor's letter
                  mustShow:
                    - Your name
                    - That you are currently unable to work
                    - The date your leave started
                    - The date you are expected to return to work
                    - Your doctor's name, practice and signature
                    - The date they issued it
                  doNotSend:
                    - A diagnosis, symptoms, treatment or medication
                    - Your medical history or records
                    - An employer leave letter
                    - Disability benefit paperwork
                  privacyNote: We only need the facts listed above. Please do not send anything else — we do not need it and we would
                    rather not hold it.
                  acceptedFormats:
                    - PDF
                    - JPEG
                    - PNG
        "400":
          $ref: "#/components/responses/Connector400Error"
      x-connector-authority: Short-lived upload session token from the exchange step
      x-connector-header: LendEasy-Upload-Session
  /v1/servicing/borrower-document-uploads/current/documents:
    post:
      tags:
        - Portal contracts
      operationId: submitBorrowerUploadDocument
      summary: Upload the requested document
      description: Accepts the one bounded upload the session authorizes as a single multipart part named file — a PDF, JPEG,
        or PNG up to the configured 10 MB ceiling — and returns a receipt. The content type is detected server-side, and
        refusals are a flat 200 with a closed borrower-safe reason.
      x-idempotent: false
      x-availability: implemented
      x-audience: connector
      x-error-contract: connector-json
      security:
        - uploadSession: []
      parameters: []
      responses:
        "201":
          description: Created.
          content:
            application/json:
              schema:
                type: object
                required:
                  - accepted
                  - receiptReference
                  - receivedAt
                  - statusMessage
                properties:
                  accepted:
                    type: boolean
                  receiptReference:
                    type: string
                  receivedAt:
                    type: string
                    format: date-time
                  statusMessage:
                    type: string
              example:
                accepted: true
                receiptReference: LE-UPL-4201
                receivedAt: 2026-07-25T18:12:00Z
                statusMessage: Thank you — we have received your document and will look at it. You do not need to do anything else.
        "400":
          $ref: "#/components/responses/Connector400Error"
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                  description: The document bytes. PDF, JPEG, or PNG up to the configured size ceiling.
              required:
                - file
      x-connector-authority: Short-lived upload session token from the exchange step
      x-connector-header: LendEasy-Upload-Session
  /v1/servicing/borrower-document-uploads/current/end:
    post:
      tags:
        - Portal contracts
      operationId: endBorrowerUploadSession
      summary: End the upload session
      description: Ends the session early so a shared device does not keep it alive. The call always reports accepted and
        requires no body.
      x-idempotent: false
      x-availability: implemented
      x-audience: connector
      x-error-contract: connector-json
      security:
        - uploadSession: []
      parameters: []
      responses:
        "200":
          description: Successful response.
          content:
            application/json:
              schema:
                type: object
                required:
                  - accepted
                  - statusMessage
                properties:
                  accepted:
                    type: boolean
                  statusMessage:
                    type: string
              example:
                accepted: true
                statusMessage: You can close this page.
        "400":
          $ref: "#/components/responses/Connector400Error"
      x-connector-authority: Short-lived upload session token from the exchange step
      x-connector-header: LendEasy-Upload-Session
components:
  securitySchemes:
    gatewaySignature:
      type: apiKey
      in: header
      name: X-LendEasy-Gateway-Signature
      description: Pinned channel-gateway signature over the exact request bytes. Ordinary client authentication is not accepted.
    voiceTaskToken:
      type: apiKey
      in: header
      name: X-LendEasy-Voice-Task-Token
      description: Short-lived task token bound to one voice dispatch and its callback audience.
    uploadLinkToken:
      type: apiKey
      in: header
      name: LendEasy-Upload-Link
      description: Single-use emailed link secret exchanged once for a bounded borrower upload session.
    uploadSession:
      type: apiKey
      in: header
      name: LendEasy-Upload-Session
      description: Short-lived borrower upload session token bound to one case, customer, and evidence purpose.
  schemas:
    ConnectorError:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: Safe connector rejection reason.
  responses:
    Connector400Error:
      description: Malformed or unsupported callback body.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ConnectorError"
          example:
            error: The callback body is invalid.
    Connector401Error:
      description: Missing, invalid, expired, or differently bound connector authority.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ConnectorError"
          example:
            error: Invalid or missing connector authority.
    Connector404Error:
      description: No visible dispatch exists.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ConnectorError"
          example:
            error: No visible voice dispatch exists.
    Connector409Error:
      description: The callback conflicts with current dispatch state or an already accepted result.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ConnectorError"
          example:
            error: The callback conflicts with current dispatch state.
