{
  "openapi": "3.1.0",
  "info": {
    "title": "Finosu API",
    "description": "Finosu API",
    "license": {
      "name": "MIT"
    },
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.finosu.com"
    }
  ],
  "security": [
    {
      "apiKeyAuth": []
    }
  ],
  "paths": {
    "/originations/close-reasons": {
      "get": {
        "summary": "List origination close reasons",
        "description": "Complete unpaginated catalog of case-sensitive origination close reasons. Requires an active X-API-Key.",
        "responses": {
          "200": {
            "description": "Accepted reasons",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "example": [
                  "FUNDED",
                  "WITHDRAWN",
                  "REJECTED"
                ]
              }
            }
          },
          "401": {
            "description": "Missing, invalid, or inactive API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/originations/{id}/close": {
      "post": {
        "summary": "Close origination",
        "description": "Record the final outcome for an enrolled application and cancel its active/paused call, SMS and email cadences and pending communications atomically. No lender-side writes. Same-reason retries reassert closure; conflicting outcomes and active funded loans return 409. Queue-claimed calls remain owned by delivery/recovery and are excluded from cancelledCalls. In-progress communications cannot be recalled.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "External application reference (loanId from enrollment)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CloseOriginationRequest"
              },
              "example": {
                "reason": "FUNDED"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Origination closed; counts reflect work cancelled by this request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloseOriginationResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, or inactive API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Application not found in the authenticated company",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Ambiguous reference, no origination enrollment, active funded loan, changed scope, or conflicting final outcome",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Missing or invalid body/reason, or unexpected fields",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "detail"
                  ],
                  "properties": {
                    "detail": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal server error; transaction rolled back",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/calls/flags": {
      "get": {
        "summary": "List call flags by task type",
        "description": "Returns the flag catalog for the requested task type, served from the flag registry \u2014 the same source call results use \u2014 so every flag that can appear in your call results is discoverable here. Scoped to the authenticated company, ordered alphabetically by name, no pagination. This lookup does not enable workflows, configure webhook delivery, or cancel servicing.",
        "parameters": [
          {
            "name": "taskType",
            "in": "query",
            "required": true,
            "description": "Case-sensitive task type from the origination or collections task-type catalog.",
            "schema": {
              "type": "string",
              "enum": [
                "PendingLoanNewLead",
                "PendingLoanReturning",
                "Collections"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Approved public integration flags for the requested task type.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, max-age=3600: private clients may cache successful responses for one hour; shared caches must not store them."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "name",
                      "description"
                    ],
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "Case-sensitive flag code used in call results."
                      },
                      "description": {
                        "type": "string",
                        "description": "Human-readable explanation of the flag."
                      }
                    }
                  }
                },
                "examples": {
                  "Illustrative": {
                    "summary": "Illustrative sample; call the endpoint for the authoritative catalog per task type",
                    "value": [
                      {
                        "name": "DNC",
                        "description": "The customer requested that calls stop."
                      },
                      {
                        "name": "WRONG_NUMBER",
                        "description": "The dialed number does not belong to the intended customer."
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, empty, invalid, inactive, or deleted API key.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "detail"
                  ],
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Missing or unsupported taskType.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "detail"
                  ],
                  "properties": {
                    "detail": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "loc",
                          "msg",
                          "type"
                        ],
                        "properties": {
                          "loc": {
                            "type": "array",
                            "items": {
                              "oneOf": [
                                {
                                  "type": "string"
                                },
                                {
                                  "type": "integer"
                                }
                              ]
                            }
                          },
                          "msg": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/loans/stop-servicing-reasons": {
      "get": {
        "summary": "List stop servicing reasons",
        "description": "Returns the complete, unpaginated array of case-sensitive reasons accepted by both loan and customer cancel-servicing endpoints. Values are shared by all authenticated companies. Additional values may be added; clients must not rely on array positions or a fixed length.",
        "responses": {
          "200": {
            "description": "Accepted stop servicing reasons",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "example": [
                  "PAID_OFF",
                  "BANKRUPTCY",
                  "FRAUD_CLAIM",
                  "MILITARY",
                  "DECEASED",
                  "INCARCERATED",
                  "DEBT_MANAGEMENT_COMPANY",
                  "NO_LOGIN_2M",
                  "NO_PAYMENT_OR_PLAN_3M",
                  "NO_PLAN_PAYMENT_2M",
                  "NO_PAYMENT_OR_PLAN_4M",
                  "SOLD_OFF",
                  "MISTAKE",
                  "DEBT_DISPUTE",
                  "COMPLAINT_TRIGGER",
                  "HISTORICAL_INELIGIBLE",
                  "DUPLICATE",
                  "REFUSES_TO_PAY",
                  "API_REQUEST",
                  "LMS_CLOSED",
                  "DATA_DEFECT_HOLD",
                  "COERCED_DEBT_CLAIM",
                  "TRANSFERRED_TO_FINOSU"
                ]
              }
            }
          },
          "401": {
            "description": "Missing, empty, invalid, or inactive API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "detail"
                  ],
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/origination/task-types": {
      "get": {
        "summary": "List origination task types",
        "description": "Returns origination task types for POST /customers/schedule/sync with scheduleMode=enroll. This catalog does not indicate account enablement. For collections use GET /collections/task-types.",
        "responses": {
          "200": {
            "description": "Origination task types",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "uniqueItems": true,
                  "items": {
                    "type": "string",
                    "enum": ["PendingLoanNewLead", "PendingLoanReturning"]
                  }
                },
                "example": ["PendingLoanNewLead", "PendingLoanReturning"]
              }
            }
          },
          "401": {
            "description": "Missing, empty, invalid, or inactive API key"
          }
        }
      }
    },
    "/collections/task-types": {
      "get": {
        "summary": "List collections task types",
        "description": "Returns collections task types for configured customer/loan boarding: create the customer, then POST /loans. This catalog does not indicate account enablement. For origination use GET /origination/task-types.",
        "responses": {
          "200": {
            "description": "Collections task types",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "uniqueItems": true,
                  "items": {
                    "type": "string",
                    "enum": ["Collections"]
                  }
                },
                "example": ["Collections"]
              }
            }
          },
          "401": {
            "description": "Missing, empty, invalid, or inactive API key"
          }
        }
      }
    },
    "/customers": {
      "get": {
        "description": "Returns all customers from the system that the user has access to",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "The maximum number of results to return",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "name": "test",
            "in": "query",
            "description": "Filter for test customers only. If true, returns only test customers. If false or omitted, returns only non-test customers.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Customer response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Customer"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Invalid pagination parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          }
        }
      },
      "post": {
        "description": "Creates a new customer",
        "requestBody": {
          "description": "Customer to add",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewCustomer"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Customer response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "description": "The company has no task types configured.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          },
          "422": {
            "description": "Invalid customer fields.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "502": {
            "description": "sync=true could not fetch valid customer data from the LMS. Malformed optional LMS birthdays are skipped. Provider failures are not request-validation errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          }
        }
      }
    },
    "/customers/batch": {
      "post": {
        "description": "Submit a list for background processing. Explicit scheduleMode=enroll uses origination enrollment with a batch Idempotency-Key and per-row idempotencyKey. Omit scheduleMode for the existing customer-create workflow. HTTP 202 confirms acceptance, not successful processing of every row.",
        "requestBody": {
          "description": "Choose origination list enrollment or legacy customer creation. Enrollment row fields are validated individually by the worker; invalid rows are reported in results.",
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "required": [
                      "customers"
                    ],
                    "properties": {
                      "customers": {
                        "type": "array",
                        "description": "Array of customer records to create",
                        "items": {
                          "$ref": "#/components/schemas/NewCustomer"
                        }
                      }
                    },
                    "title": "Create Customers",
                    "not": {
                      "required": [
                        "scheduleMode"
                      ]
                    }
                  },
                  {
                    "$ref": "#/components/schemas/OriginationBatchRequest"
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Batch job accepted for async processing. Use GET /customers/batch/{jobId} to check status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobId": {
                      "type": "string",
                      "description": "Unique identifier for the batch job. Use this to check status via GET /customers/batch/{jobId}",
                      "example": "550e8400-e29b-41d4-a716-446655440000"
                    },
                    "totalCustomers": {
                      "type": "integer",
                      "description": "Total number of customers queued for processing",
                      "example": 100
                    },
                    "message": {
                      "type": "string",
                      "description": "Status message with instructions for checking progress",
                      "example": "Batch job created with 4 parallel batches. Use GET /customers/batch/550e8400-e29b-41d4-a716-446655440000 to check status."
                    }
                  }
                }
              }
            },
            "headers": {
              "Location": {
                "description": "Enrollment: company-scoped batch status URL.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotency-Replayed": {
                "description": "Enrollment: true when returning an existing batch job.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "Enrollment: no-store.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Requested origination workflow is not enabled for this company. Validation errors on customer-create rows return 422.",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/HTTPError"
                    },
                    {
                      "$ref": "#/components/schemas/OriginationEnrollmentError"
                    }
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Enrollment batch key was used for a different or archived list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OriginationEnrollmentError"
                }
              }
            }
          },
          "413": {
            "description": "Enrollment list exceeds 10 MiB of serialized customer data.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OriginationEnrollmentError"
                }
              }
            }
          },
          "422": {
            "description": "Invalid envelope or missing/invalid batch idempotency key. Enrollment field errors inside valid row objects are reported asynchronously per row.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EnrollmentValidationErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Enrollment batch submission quota exceeded (10 per minute per company).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OriginationEnrollmentError"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "503": {
            "description": "Enrollment admission or dispatch unavailable. Retry the identical list and header key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OriginationEnrollmentError"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or inactive API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "summary": "Customer Batch Intake",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Required for scheduleMode=enroll. Stable key for this exact ordered list. Separate from each row idempotencyKey.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9._:-]{1,128}$"
            }
          }
        ]
      }
    },
    "/customers/batch/{jobId}": {
      "get": {
        "summary": "Batch Progress and Results",
        "description": "Company-scoped job progress. Origination jobs return ordered per-row results and failures; customer-create jobs return failures with results empty. Partial while running. completed can include failed rows, so always inspect failed and the row outcomes.",
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "description": "Job ID returned by POST /customers/batch",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Batch job status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerBatchStatusResponse"
                }
              }
            }
          },
          "404": {
            "description": "No customer batch job with that ID for the authenticated company",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Job ID is not a valid UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/customers/schedule/sync": {
      "post": {
        "summary": "Customer Schedule Sync",
        "description": "Sync a customer schedule. Omit scheduleMode or use replace for the existing replacement behavior. Explicitly use enroll for company-enabled application enrollment that preserves cadence progress and requires an Idempotency-Key. The same customer id with a new loanId creates a separate application and cadence without changing previous applications. Use a new key per application; customer contact restrictions and call spacing still apply.",
        "requestBody": {
          "description": "Choose the enrollment schema for scheduleMode=enroll (requires Idempotency-Key), or the replacement schema for scheduleMode=replace or omitted. See the Origination guide for enrollment examples.",
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/SyncCustomerSchedule"
                  },
                  {
                    "$ref": "#/components/schemas/OriginationEnrollmentRequest"
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Customer schedule synced successfully",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/SyncScheduleResponse"
                    },
                    {
                      "$ref": "#/components/schemas/OriginationSyncScheduleResponse"
                    }
                  ]
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "description": "Enrollment mode only: true when returning a saved receipt.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true",
                    "false"
                  ]
                }
              },
              "Location": {
                "description": "Enrollment mode only: URL for the recorded first/next scheduled call.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Replacement: existing Do Not Call conflict, unsupported company workflow, or no configured task types. Enrollment: workflow is not enabled for the company.",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/HTTPError"
                    },
                    {
                      "$ref": "#/components/schemas/OriginationEnrollmentError"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, empty, invalid, or inactive API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["detail"],
                  "properties": {"detail": {"type": "string"}}
                }
              }
            }
          },
          "409": {
            "description": "Enrollment conflict or request in progress. For request_in_progress, retry the same key after Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OriginationEnrollmentError"
                }
              }
            }
          },
          "422": {
            "description": "Invalid request. Enrollment mode returns a sanitized error without the submitted values.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EnrollmentValidationErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Enrollment request quota exceeded (60/minute/company). Honor Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OriginationEnrollmentError"
                }
              }
            }
          },
          "503": {
            "description": "Enrollment temporarily unavailable. Retry the same key and payload after Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OriginationEnrollmentError"
                }
              }
            }
          },
          "502": {
            "description": "sync=true could not fetch valid customer data from the LMS. Malformed optional LMS birthdays are skipped. Provider failures are not request-validation errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Required only for scheduleMode=enroll. Reuse this key and the same payload when retrying. Scoped to your API-key company.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9._:-]{1,128}$",
              "minLength": 1,
              "maxLength": 128
            }
          }
        ]
      }
    },
    "/customers/{id}": {
      "get": {
        "description": "Returns a specific customer from the system that the user has access to",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID of customer to retrieve",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Customer response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          }
        }
      },
      "put": {
        "description": "Updates a customer",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID of customer to update",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "requestBody": {
          "description": "Customer to update",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Customer response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          },
          "422": {
            "description": "Invalid customer fields, including phone or state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "description": "Deletes a customer",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID of customer to delete",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Customer response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          }
        }
      }
    },
    "/customers/{id}/cancel-communications": {
      "post": {
        "description": "Cancels all pending communications for a customer: scheduled calls, scheduled emails, scheduled SMS, and all active schedules",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID of customer whose communications should be canceled",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "All communications canceled for customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CancelCommunicationsResponse"
                }
              }
            }
          },
          "404": {
            "description": "Customer not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/customers/{id}/cancel-servicing": {
      "post": {
        "description": "Cancel servicing for all loans belonging to a customer. Sets all loans' servicing status to NOT_SERVICING and cancels all pending scheduled payments, calls, emails, and SMS across all loans.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "External reference ID of the customer",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Optional reason for stopping servicing. Defaults to API_REQUEST if not specified.",
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CancelLoanServicingRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Customer servicing cancelled successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CancelCustomerServicingResponse"
                }
              }
            }
          },
          "422": {
            "description": "Invalid cancellation reason or request body",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["detail"],
                  "properties": {
                    "detail": {
                      "oneOf": [
                        { "type": "string" },
                        {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": ["loc", "msg", "type"],
                            "properties": {
                              "loc": {
                                "type": "array",
                                "items": { "oneOf": [{ "type": "string" }, { "type": "integer" }] }
                              },
                              "msg": { "type": "string" },
                              "type": { "type": "string" }
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Customer not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/billing/daily-costs": {
      "get": {
        "operationId": "listDailyBillingCosts",
        "summary": "List daily billing costs",
        "description": "Returns one row per loan with billable activity on a completed America/New_York calendar day: calls, texts, emails, and payments, priced the same way as the Billing Summary in the client dashboard. Scoped to the company of the X-API-Key; a lender with several brands calls once per brand key. Only first-party loans on usage billing are included. Ordered by loanId, then the Finosu loan UUID. Limited to 60 requests per 60 seconds per company across all API keys. Exceeded quotas return 429; quota-service outages return 503. Both include Retry-After: 60. Follow nextCursor until null; keep date unchanged.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "date",
            "in": "query",
            "required": true,
            "description": "Calendar day in America/New_York, YYYY-MM-DD. Must be a completed day: today or a future date returns 422.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum rows per page. Default 1000; range 1–1000.",
            "schema": {
              "type": "integer",
              "default": 1000,
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque nextCursor from the preceding response. Keep the same company and date.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2048
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of per-loan daily costs. nextCursor is null on the last page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DailyCostsResponse"
                }
              }
            }
          },
          "400": {
            "description": "The company has no billing configuration, or is not billed per use.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "detail"
                  ],
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, inactive, deleted, or empty API key.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "detail"
                  ],
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Missing, malformed, current, or future date; or an invalid cursor or one issued for a different company or date.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Company quota exceeded. Retry after the indicated number of seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 60
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "detail"
                  ],
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Quota verification is unavailable. Retry after the indicated number of seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 60
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "detail"
                  ],
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected internal error."
          }
        }
      }
    },
    "/calls": {
      "get": {
        "operationId": "listCalls",
        "summary": "List all calls",
        "description": "Returns call history across the authenticated company, ordered by creation time and call UUID descending. Includes all dates, call types, test customers, processing stages, and unsuccessful attempts. Deleted calls/customers and legacy calls without a creation timestamp are excluded. Limited to 60 requests per 60 seconds per company across all API keys. Exceeded quotas return 429; quota-service outages return 503. Both include Retry-After: 60. Follow nextCursor until null; keep filters unchanged. Cursors do not provide snapshot isolation.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum records per page. Default 50; range 1–100.",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque nextCursor from the preceding response. Keep the same company and filters.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2048
            }
          },
          {
            "name": "customerId",
            "in": "query",
            "required": false,
            "description": "Exact external customer reference. Unknown references return an empty list.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            }
          },
          {
            "name": "callType",
            "in": "query",
            "required": false,
            "description": "Filter by call type; omit for all types.",
            "schema": {
              "type": "string",
              "enum": [
                "INBOUND",
                "OUTBOUND",
                "UPLOAD"
              ]
            }
          },
          {
            "name": "createdAfter",
            "in": "query",
            "required": false,
            "description": "Inclusive creation-time lower bound. Must include a timezone and precede createdBefore.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "createdBefore",
            "in": "query",
            "required": false,
            "description": "Exclusive creation-time upper bound. Must include a timezone.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of calls, newest first. nextCursor is null on the last page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallListResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, inactive, deleted, or empty API key.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "detail"
                  ],
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Invalid query parameter, cursor, cursor scope/filter combination, or date range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Company quota exceeded. Retry after the indicated number of seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 60
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "detail"
                  ],
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Quota verification is unavailable. Retry after the indicated number of seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 60
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "detail"
                  ],
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected internal error."
          }
        }
      }
    },
    "/calls/{id}": {
      "get": {
        "description": "Returns a specific call in the authenticated company. recordingUrl is a fresh one-hour link or null if unavailable. Missing, deleted, or inaccessible calls return 404.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID of call to retrieve",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Call response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          },
          "422": {
            "description": "Call ID is not a valid UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/calls/customer/{customerId}": {
      "get": {
        "description": "Returns a customer's calls from the last 30 days, newest first. For older history use GET /calls.",
        "parameters": [
          {
            "name": "customerId",
            "in": "path",
            "description": "External customer reference ID",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "The maximum number of results to return",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "The offset of the first result to return",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Calls response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CallResponse"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Invalid pagination parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          },
          "404": {
            "description": "No customer with that reference ID in the authenticated company",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/texts/{customerId}": {
      "get": {
        "description": "Returns all texts for a given customer",
        "parameters": [
          {
            "name": "customerId",
            "in": "path",
            "description": "ID of customer",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "The maximum number of results to return",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "The offset of the first result to return",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Customer response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TextResponse"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Invalid pagination parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          }
        }
      }
    },
    "/scheduled-calls/{customerId}": {
      "post": {
        "description": "Creates scheduled calls for a customer. For origination enrollment use POST /customers/schedule/sync; for configured loan-based collections boarding use POST /loans.",
        "parameters": [
          {
            "name": "customerId",
            "in": "path",
            "description": "ID of customer",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Required scheduling configuration for existing integrations.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "scheduleType": {
                    "type": "string",
                    "description": "Configured schedule identifier. Use the value assigned to your integration; unsupported values are rejected. This is not a taskType from GET /origination/task-types."
                  }
                },
                "required": ["scheduleType"]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Array of scheduled calls created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ScheduledCall"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          },
          "422": {
            "description": "Invalid schedule type, missing/invalid customer timezone, missing state, or invalid request fields.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No customer with that reference ID in the authenticated company",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/scheduled-calls/{id}": {
      "get": {
        "description": "Returns a specific scheduled call from the system that the user has access to",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID of scheduled call to retrieve",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Scheduled call response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledCall"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          },
          "422": {
            "description": "Scheduled call ID is not a valid UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Scheduled call not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scheduled call belongs to another company",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/scheduled-calls/{id}/cancel": {
      "post": {
        "description": "Cancels a specific scheduled call by ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "ID of scheduled call to cancel",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Scheduled call canceled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledCall"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          },
          "422": {
            "description": "Scheduled call ID is not a valid UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Scheduled call not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scheduled call belongs to another company",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/scheduled-calls/customer/{customerId}/cancel-all": {
      "post": {
        "description": "Cancels all pending scheduled calls for a customer",
        "parameters": [
          {
            "name": "customerId",
            "in": "path",
            "description": "ID of customer whose scheduled calls should be canceled",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "All scheduled calls canceled for customer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "canceledCount": {
                      "type": "integer",
                      "description": "Number of scheduled calls that were canceled"
                    },
                    "scheduledCalls": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ScheduledCall"
                      },
                      "description": "Array of canceled scheduled calls"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          },
          "404": {
            "description": "No customer with that reference ID in the authenticated company",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/{customerId}": {
      "get": {
        "description": "Returns email history for a customer. limit and offset select email threads before their messages are flattened, so the number of returned messages may exceed limit.",
        "parameters": [
          {
            "name": "customerId",
            "in": "path",
            "description": "ID of customer",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "The maximum number of results to return",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "The offset of the first result to return",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Customer response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/EmailResponse"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Invalid pagination parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          }
        }
      }
    },
    "/scheduled-calls/customer/{customerId}": {
      "get": {
        "description": "Returns all scheduled calls for a given customer",
        "parameters": [
          {
            "name": "customerId",
            "in": "path",
            "description": "ID of customer",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Scheduled calls response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ScheduledCall"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          },
          "404": {
            "description": "No customer with that reference ID in the authenticated company",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/ach-returns": {
      "post": {
        "description": "Receive ACH return information and update the matching payment records.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AchReturnRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Payment update recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AchReturnResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, empty, invalid, or inactive X-API-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid request, including a payment that does not belong to the specified loan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Customer, loan, or ACH payment not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Multiple matching ACH payments.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Error during processing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Return-note delivery is already in progress. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/ach-returns/batch": {
      "post": {
        "description": "Process a batch independently and inspect each result; HTTP 200 does not mean every item succeeded.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AchReturnBatchRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Batch processing results.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AchReturnBatchResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, empty, invalid, or inactive X-API-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid batch request schema. Nothing was processed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKeyAuth": []
          }
        ]
      }
    },
    "/loans": {
      "post": {
        "description": "Create or update (upsert) a loan pushed from an external partner. The loan is keyed on (customer, externalReference). Returns 201 on create, 200 on update.",
        "requestBody": { "description": "Loan data", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NewLoan" } } }, "required": true },
        "responses": {
          "201": { "description": "Loan created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Loan" } } } },
          "200": { "description": "Existing loan updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Loan" } } } },
          "404": { "description": "Customer not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "409": { "description": "Concurrent duplicate — another request created the same loan", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "422": {
            "description": "Invalid loan fields, dates, portfolio, enums, timezone, task type, servicingStatus on create, or changed immutable amountDue.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/loans/batch": {
      "post": {
        "summary": "Create Loans (Batch)",
        "description": "Create or update (upsert) many loans asynchronously. Large batches are split server-side into parallel slices of 25 rows. Returns a job ID immediately; poll GET /loans/batch/{jobId} for progress and for every row that failed. Each row has the same semantics as POST /loans, including the upsert on (customerId, id), so resubmitting a batch updates rather than duplicates.",
        "requestBody": {
          "description": "Batch of loan records",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["loans"],
                "properties": {
                  "loans": { "type": "array", "minItems": 1, "description": "Array of loan records to create. Every (customerId, id) pair must be unique within the request.", "items": { "$ref": "#/components/schemas/NewLoan" } }
                }
              }
            }
          }
        },
        "responses": {
          "202": { "description": "Batch job accepted for async processing. Use GET /loans/batch/{jobId} to check status.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoanBatchResponse" } } } },
          "422": {
            "description": "Invalid row schema, empty loans array, or duplicate customer/loan pair. Nothing was queued; business validation failures after admission are reported per row.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "500": { "description": "Could not store or queue the batch. Nothing was processed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/loans/batch/{jobId}": {
      "get": {
        "summary": "Get Loan Batch Status",
        "description": "Get the status of a batch loan job plus every row that failed. Successful rows are not enumerated. The failures array fills in as slices finish, so it is partial while status is processing, and is always sorted by submitted index.",
        "parameters": [{ "name": "jobId", "in": "path", "description": "Job ID returned by POST /loans/batch", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": {
          "200": { "description": "Batch job status", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoanBatchStatusResponse" } } } },
          "404": { "description": "No loan batch job with that ID for the authenticated company", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "422": {
            "description": "Job ID is not a valid UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/customers/{customerId}/payment-methods": {
      "get": {
        "summary": "List Customer Payment Methods",
        "description": "List non-deleted payment-method identities for a live customer in the authenticated company. Includes inactive methods. Returns masked display fields only; status does not establish ACH authorization. Results are ordered by payment-method UUID, with SQL pagination.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "customerId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "External customer reference ID"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Payment methods; an empty array means no methods on this page.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PublicPaymentMethodResponse"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, or inactive API key"
          },
          "404": {
            "description": "Customer not found for the authenticated company"
          },
          "422": {
            "description": "Invalid pagination parameters"
          }
        }
      }
    },
    "/customers/{customerId}/payment-methods/{paymentMethodId}/ach-authorization": {
      "post": {
        "summary": "Update ACH Authorization Revocation",
        "description": "Updates revocation on one exact tenant-owned bank payment method. true revokes the current authorization. false never restores revoked consent; a new authorization must be captured. POST and PUT are equivalent. To revoke multiple bank authorizations, call separately for each BANK_ACCOUNT ID belonging to the same customer; cards are rejected and the calls are not atomic.",
        "parameters": [
          {
            "name": "customerId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "External customer reference ID"
          },
          {
            "name": "paymentMethodId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Finosu UUID of the specific bank-account payment method belonging to customerId. Not your LMS's payment-method ID, bank account number, or loan ID. Retrieve it from GET /customers/{customerId}/payment-methods using the same customer reference and API key; choose the exact BANK_ACCOUNT entry."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateACHAuthorization"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Authorization revocation status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ACHAuthorizationStatus"
                }
              }
            }
          },
          "404": {
            "description": "Customer/payment method not found for authenticated company"
          },
          "409": {
            "description": "No active authorization exists to revoke"
          },
          "422": {
            "description": "Invalid method type or attempted consent restoration"
          }
        }
      },
      "put": {
        "summary": "Update ACH Authorization Revocation",
        "description": "Updates revocation on one exact tenant-owned bank payment method. true revokes the current authorization. false never restores revoked consent; a new authorization must be captured.",
        "parameters": [
          { "name": "customerId", "in": "path", "required": true, "schema": { "type": "string" }, "description": "External customer reference ID" },
          { "name": "paymentMethodId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, "description": "Finosu UUID of the specific bank-account payment method belonging to customerId. Not your LMS's payment-method ID, bank account number, or loan ID. Retrieve it from GET /customers/{customerId}/payment-methods using the same customer reference and API key; choose the exact BANK_ACCOUNT entry." }
        ],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateACHAuthorization" } } } },
        "responses": {
          "200": { "description": "Authorization revocation status", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ACHAuthorizationStatus" } } } },
          "404": { "description": "Customer/payment method not found for authenticated company" },
          "409": { "description": "No active authorization exists to revoke" },
          "422": { "description": "Invalid method type or attempted consent restoration" }
        }
      }
    },
    "/loans/{id}": {
      "put": {
        "description": "Partially update a loan by its external reference. Only send fields that changed. amountDue is immutable.",
        "parameters": [{ "name": "id", "in": "path", "description": "External reference ID of the loan", "required": true, "schema": { "type": "string" } }],
        "requestBody": { "description": "Fields to update", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateLoan" } } }, "required": true },
        "responses": {
          "409": { "description": "Ambiguous loan, conflicting/reordered balance update, or legacy balance change after feed enrollment", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "200": { "description": "Loan updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Loan" } } } },
          "404": { "description": "Loan not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "422": {
            "description": "Invalid loan fields, dates, or enum values.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "description": "Soft-delete a loan. Sets servicing status to NOT_SERVICING, cancels scheduled payments and communications.",
        "parameters": [{ "name": "id", "in": "path", "description": "External reference ID of the loan", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Loan deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeleteLoanResponse" } } } },
          "404": { "description": "Loan not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "500": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/loans/{id}/cancel-servicing": {
      "post": {
        "description": "Cancel loan servicing and all associated scheduled payments and communications. Sets the loan's servicing status to NOT_SERVICING and cancels all pending scheduled payments, calls, emails, and SMS.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "External reference ID of the loan to cancel servicing for",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Optional reason for stopping servicing. Defaults to API_REQUEST if not specified.",
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CancelLoanServicingRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Loan servicing cancelled successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CancelLoanServicingResponse"
                }
              }
            }
          },
          "422": {
            "description": "Invalid cancellation reason or request body",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["detail"],
                  "properties": {
                    "detail": {
                      "oneOf": [
                        { "type": "string" },
                        {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": ["loc", "msg", "type"],
                            "properties": {
                              "loc": {
                                "type": "array",
                                "items": { "oneOf": [{ "type": "string" }, { "type": "integer" }] }
                              },
                              "msg": { "type": "string" },
                              "type": { "type": "string" }
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Loan not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks/customers/upsert": {
      "post": {
        "description": "Your system posts customer data to Finosu. This is an inbound API endpoint, not an outbound callback.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "description": "Customer data to upsert",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerUpsertWebhook"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Customer upserted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerUpsertResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, empty, invalid, or inactive X-API-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string",
                      "example": "Internal server error processing customer webhook"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Invalid customer input, including name, birthday, or phone.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/external-payments": {
      "post": {
        "summary": "Record a settled external payment",
        "description": "Record money already collected and settled in a lender/LMS. Requires the exact company-owned loan/account identifier. This creates no Finosu processor charge. The company-scoped transactionId is the atomic idempotency identity: exact retries return 200 and the original transaction; changed payloads return 409. Only settled USD payments are supported; pending, returns, reversals, refunds, negative adjustments, and corrections are rejected.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublicExternalPaymentRequest"
              },
              "example": {
                "loanId": "ACCOUNT-1001",
                "transactionId": "lms-payment-001",
                "sourceSystem": "example-lms",
                "amount": "125.25",
                "currency": "USD",
                "paymentDate": "2026-10-16",
                "status": "settled",
                "settlesLoan": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "External payment recorded and applied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicExternalPaymentResponse"
                }
              }
            }
          },
          "200": {
            "description": "Exact idempotent replay; no accounting or settlement effects were repeated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicExternalPaymentResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, empty, invalid, or inactive X-API-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The exact loan/account identifier is not owned by the authenticated company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Ambiguous loan identifier or transactionId reused with a different payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid identifier, decimal amount, currency, date, status, or request shape.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "The atomic payment/accounting operation failed and was rolled back.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/ach-confirms": {
      "post": {
        "description": "Confirm settlement of an existing ACH payment.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AchConfirmRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Payment update recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AchConfirmResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, empty, invalid, or inactive X-API-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid request, including a payment that does not belong to the specified loan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Customer, loan, or ACH payment not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Multiple matching ACH payments.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Error during processing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/ach-confirms/batch": {
      "post": {
        "description": "Process a batch independently and inspect each result; HTTP 200 does not mean every item succeeded.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AchConfirmBatchRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Batch processing results.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AchConfirmBatchResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, empty, invalid, or inactive X-API-Key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Missing or invalid request fields",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPError"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks/lms/sync/customer-full": {
      "post": {
        "summary": "Sync customer and loans from LMS",
        "description": "Perform a full sync of a customer and all their loans from the company's configured LMS.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Optional correlation key, logged for observability only. Does not deduplicate requests or replay previous results.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Customer identifier to sync",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LmsWebhookSyncRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sync result returned. Inspect success, error, and per-loan results.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LmsWebhookSyncResponse"
                }
              }
            }
          },
          "400": {
            "description": "Configured LMS does not support customer-level sync.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Company not authorized for LMS integration",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Requested customer or loan confirmed missing from the LMS.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected provider or internal failure, including malformed provider data.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Request-body validation failed or identifier is invalid for the configured LMS.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks/lms/sync/loan": {
      "post": {
        "summary": "Sync a single loan from LMS",
        "description": "Sync a single loan and its associated customer from the company's configured LMS. This is a loan-centric entry point for LMS platforms where customer lookups don't reveal associated loans.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Optional correlation key, logged for observability only. Does not deduplicate requests or replay previous results.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Loan identifier to sync",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LmsLoanSyncRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sync result returned. Inspect success, error, and per-loan results.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LmsLoanSyncResponse"
                }
              }
            }
          },
          "400": {
            "description": "LMS handler does not support loan-level sync",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Company not authorized for LMS integration",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Requested customer or loan confirmed missing from the LMS.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected provider or internal failure, including malformed provider data.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Request-body validation failed or identifier is invalid for the configured LMS.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks/lms/sync/customer-full/batch": {
      "post": {
        "summary": "Batch sync customers and loans from LMS",
        "description": "Queue 1–100 external customer IDs for background processing. HTTP 202 acknowledges the job, not successful synchronization.",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LmsBatchSyncRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Batch job queued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LmsBatchSyncResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Company not authorized for LMS integration.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid customer ID list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Failed to store or queue batch data.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks/lms/sync/batch/{jobId}": {
      "get": {
        "summary": "Get batch LMS sync job status",
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "description": "Job UUID returned when the batch was queued.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current job progress; inspect status and failure counts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LmsBatchStatusResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Job not found for this company.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid job UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "/call/webhook": {
      "post": {
        "description": "Finosu posts call results to your configured receiver. /call/webhook is a documentation identifier, not an endpoint to call on api.finosu.com. Acknowledge with any 2xx status after durably accepting the event.",
        "requestBody": {
          "description": "Call result delivery",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewCall"
              }
            }
          },
          "required": true
        },
        "responses": {
          "2XX": {
            "description": "Any 2xx acknowledges the delivery. Non-2xx responses and transport errors are retried within configured limits."
          }
        },
        "security": [],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Opaque stable key for this call and destination, reused across retries. Persist the key and durably accept each delivery once; the header alone does not provide exactly-once processing."
          }
        ]
      }
    },
    "/transfer-note": {
      "post": {
        "description": "Submit transfer note with call summary and verification details",
        "requestBody": {
          "description": "Transfer note data",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TransferNote"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return a 200 status to indicate that the data was received successfully"
          }
        },
        "security": []
      }
    }
  },
  "components": {
    "schemas": {
      "OriginationCloseReason": {
        "type": "string",
        "enum": [
          "FUNDED",
          "WITHDRAWN",
          "REJECTED"
        ],
        "title": "OriginationCloseReason"
      },
      "CloseOriginationRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "reason"
        ],
        "properties": {
          "reason": {
            "$ref": "#/components/schemas/OriginationCloseReason"
          }
        }
      },
      "CloseOriginationResponse": {
        "type": "object",
        "required": [
          "id",
          "reason",
          "cancelledCallSchedules",
          "cancelledEmailSchedules",
          "cancelledSmsSchedules",
          "cancelledCalls",
          "cancelledEmails",
          "cancelledSms"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "const": "CLOSED",
            "default": "CLOSED"
          },
          "reason": {
            "$ref": "#/components/schemas/OriginationCloseReason"
          },
          "cancelledCallSchedules": {
            "type": "integer"
          },
          "cancelledEmailSchedules": {
            "type": "integer"
          },
          "cancelledSmsSchedules": {
            "type": "integer"
          },
          "cancelledCalls": {
            "type": "integer"
          },
          "cancelledEmails": {
            "type": "integer"
          },
          "cancelledSms": {
            "type": "integer"
          }
        }
      },
      "OriginationBatchItem": {
        "additionalProperties": false,
        "properties": {
          "scheduleMode": {
            "const": "enroll",
            "default": "enroll",
            "title": "Schedulemode",
            "type": "string"
          },
          "taskType": {
            "enum": [
              "PendingLoanNewLead",
              "PendingLoanReturning"
            ],
            "title": "Tasktype",
            "type": "string"
          },
          "id": {
            "maxLength": 255,
            "minLength": 1,
            "title": "Id",
            "type": "string"
          },
          "loanId": {
            "maxLength": 255,
            "minLength": 1,
            "title": "Loanid",
            "type": "string"
          },
          "birthday": {
            "format": "date",
            "title": "Birthday",
            "type": "string"
          },
          "ssnLastFour": {
            "pattern": "^[0-9]{4}$",
            "title": "Ssnlastfour",
            "type": "string"
          },
          "firstName": {
            "maxLength": 100,
            "minLength": 1,
            "title": "Firstname",
            "type": "string"
          },
          "lastName": {
            "maxLength": 100,
            "minLength": 1,
            "title": "Lastname",
            "type": "string"
          },
          "middleName": {
            "anyOf": [
              {
                "maxLength": 100,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Middlename"
          },
          "streetAddress1": {
            "anyOf": [
              {
                "maxLength": 255,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Streetaddress1"
          },
          "streetAddress2": {
            "anyOf": [
              {
                "maxLength": 255,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Streetaddress2"
          },
          "city": {
            "anyOf": [
              {
                "maxLength": 100,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "City"
          },
          "state": {
            "anyOf": [
              {
                "pattern": "^[A-Z]{2}$",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "State"
          },
          "zipCode": {
            "anyOf": [
              {
                "pattern": "^[0-9]{5}(?:-[0-9]{4})?$",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Zipcode"
          },
          "email": {
            "anyOf": [
              {
                "format": "email",
                "maxLength": 254,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Email"
          },
          "phoneNumber": {
            "anyOf": [
              {
                "maxLength": 32,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Phonenumber"
          },
          "mobilePhone": {
            "anyOf": [
              {
                "maxLength": 32,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Mobilephone"
          },
          "daysSincePaidOff": {
            "anyOf": [
              {
                "maximum": 36500,
                "minimum": 0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Dayssincepaidoff"
          },
          "daysInApplied": {
            "anyOf": [
              {
                "maximum": 36500,
                "minimum": 0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Daysinapplied"
          },
          "oneOff": {
            "default": false,
            "title": "Oneoff",
            "type": "boolean"
          },
          "timezone": {
            "anyOf": [
              {
                "maxLength": 100,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Timezone"
          },
          "isTest": {
            "default": false,
            "description": "Mark the enrollment as a test record.",
            "title": "Is Test",
            "type": "boolean"
          },
          "sync": {
            "const": false,
            "default": false,
            "title": "Sync",
            "type": "boolean"
          },
          "doNotCall": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Donotcall"
          },
          "externalMetadata": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Externalmetadata"
          },
          "idempotencyKey": {
            "pattern": "^[A-Za-z0-9._:-]{1,128}$",
            "title": "Idempotencykey",
            "type": "string"
          }
        },
        "required": [
          "taskType",
          "id",
          "loanId",
          "birthday",
          "ssnLastFour",
          "firstName",
          "lastName",
          "idempotencyKey"
        ],
        "title": "Origination Enrollment Row",
        "type": "object"
      },
      "OriginationBatchResult": {
        "properties": {
          "index": {
            "title": "Index",
            "type": "integer"
          },
          "customerId": {
            "title": "Customerid",
            "type": "string"
          },
          "loanId": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Loanid"
          },
          "status": {
            "enum": [
              "scheduled",
              "already_scheduled",
              "failed"
            ],
            "title": "Status",
            "type": "string"
          },
          "replayed": {
            "default": false,
            "title": "Replayed",
            "type": "boolean"
          },
          "retryable": {
            "default": false,
            "title": "Retryable",
            "type": "boolean"
          },
          "code": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Code"
          },
          "message": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Message"
          },
          "origination": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/OriginationScheduleReceipt"
              },
              {
                "type": "null"
              }
            ],
            "default": null
          }
        },
        "required": [
          "index",
          "customerId",
          "status"
        ],
        "title": "OriginationBatchResult",
        "type": "object"
      },
      "OriginationBatchRequest": {
        "title": "Enroll an Origination List",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "scheduleMode",
          "customers"
        ],
        "properties": {
          "scheduleMode": {
            "type": "string",
            "const": "enroll"
          },
          "customers": {
            "type": "array",
            "minItems": 1,
            "maxItems": 10000,
            "description": "Enrollment rows. Required row fields are validated independently after acceptance. Up to 10 MiB of serialized customer data.",
            "items": {
              "$ref": "#/components/schemas/OriginationBatchItem"
            }
          }
        }
      },
      "DailyLoanCost": {
        "type": "object",
        "description": "One loan's charges for one America/New_York calendar day. Numeric values are decimal strings: counts, minutes, seconds, and collected amounts have 2 decimal places; charges have 4.",
        "required": [
          "loanId",
          "customerId",
          "applicationStatus",
          "callCount",
          "callMinutes",
          "callSeconds",
          "telephonyCost",
          "virtualAgentCost",
          "smsCount",
          "smsSegments",
          "smsCost",
          "emailCount",
          "emailCost",
          "portalCollected",
          "externalCollected",
          "paymentProcessingFee",
          "totalCost"
        ],
        "properties": {
          "loanId": {
            "type": "string",
            "description": "The loan's external reference: the loan identifier you supplied.",
            "example": "5800001"
          },
          "customerId": {
            "type": "string",
            "description": "The customer's external reference, or the Finosu customer UUID when none is stored.",
            "example": "6600001"
          },
          "applicationStatus": {
            "type": "string",
            "enum": [
              "PENDING",
              "APPROVED",
              "FUNDED",
              "WITHDRAWN",
              "REJECTED"
            ],
            "description": "The loan's application status when the request is made, not on date."
          },
          "callCount": {
            "type": "string",
            "pattern": "^-?\\d+\\.\\d+$",
            "description": "Calls placed, including calls that did not connect. Charged to the loan it was scheduled for when that loan is one of the customer's loans open that day; otherwise shared evenly across those loans.",
            "example": "3.00"
          },
          "callMinutes": {
            "type": "string",
            "pattern": "^-?\\d+\\.\\d+$",
            "description": "Billed telephony minutes: each call rounded up to the next whole minute.",
            "example": "4.00"
          },
          "callSeconds": {
            "type": "string",
            "pattern": "^-?\\d+\\.\\d+$",
            "description": "Exact call seconds, the basis for virtualAgentCost.",
            "example": "179.00"
          },
          "telephonyCost": {
            "type": "string",
            "pattern": "^-?\\d+\\.\\d+$",
            "description": "callMinutes priced at the telephony rate.",
            "example": "0.1200"
          },
          "virtualAgentCost": {
            "type": "string",
            "pattern": "^-?\\d+\\.\\d+$",
            "description": "callSeconds priced at the company's tiered virtual-agent rate for the month to date.",
            "example": "0.3282"
          },
          "smsCount": {
            "type": "string",
            "pattern": "^-?\\d+\\.\\d+$",
            "description": "Sent outbound texts. Charged to the loan it was scheduled for when that loan is one of the customer's loans open that day; otherwise shared evenly across those loans.",
            "example": "1.00"
          },
          "smsSegments": {
            "type": "string",
            "pattern": "^-?\\d+\\.\\d+$",
            "description": "Billed SMS segments. A long message is sent as several segments.",
            "example": "1.00"
          },
          "smsCost": {
            "type": "string",
            "pattern": "^-?\\d+\\.\\d+$",
            "description": "Segment charges plus carrier fees passed through at cost.",
            "example": "0.0090"
          },
          "emailCount": {
            "type": "string",
            "pattern": "^-?\\d+\\.\\d+$",
            "description": "Sent emails. Charged to the loan it was scheduled for when that loan is one of the customer's loans open that day; otherwise shared evenly across those loans.",
            "example": "1.00"
          },
          "emailCost": {
            "type": "string",
            "pattern": "^-?\\d+\\.\\d+$",
            "description": "emailCount priced at the email rate.",
            "example": "0.0010"
          },
          "portalCollected": {
            "type": "string",
            "pattern": "^-?\\d+\\.\\d+$",
            "description": "Successful payments collected through Finosu on date. Decreases if a payment is later returned.",
            "example": "0.00"
          },
          "externalCollected": {
            "type": "string",
            "pattern": "^-?\\d+\\.\\d+$",
            "description": "Successful payments recorded as collected outside Finosu after placement. Not charged.",
            "example": "0.00"
          },
          "paymentProcessingFee": {
            "type": "string",
            "pattern": "^-?\\d+\\.\\d+$",
            "description": "portalCollected multiplied by the company's payment-processing rate.",
            "example": "0.0000"
          },
          "totalCost": {
            "type": "string",
            "pattern": "^-?\\d+\\.\\d+$",
            "description": "telephonyCost + virtualAgentCost + smsCost + emailCost + paymentProcessingFee.",
            "example": "0.4582"
          }
        }
      },
      "DailyCostsResponse": {
        "type": "object",
        "required": [
          "date",
          "currency",
          "rows",
          "nextCursor"
        ],
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "description": "The requested day, America/New_York."
          },
          "currency": {
            "type": "string",
            "const": "USD"
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DailyLoanCost"
            }
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque continuation cursor, or null when no more rows remain."
          }
        }
      },
      "CallListItem": {
        "type": "object",
        "required": [
          "id",
          "customerId",
          "fromPhoneNumber",
          "toPhoneNumber",
          "timestamp",
          "callType",
          "status",
          "callResult",
          "duration",
          "transcript"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Stable Finosu call ID, accepted by GET /calls/{id}."
          },
          "customerId": {
            "type": "string",
            "description": "External customer reference, or Finosu customer UUID when absent."
          },
          "fromPhoneNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": "Caller phone number when available."
          },
          "toPhoneNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": "Callee phone number when available."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "Call record creation time."
          },
          "callType": {
            "type": "string",
            "enum": [
              "INBOUND",
              "OUTBOUND",
              "UPLOAD"
            ]
          },
          "status": {
            "type": "string",
            "description": "Current call processing stage.",
            "example": "CALL_ENDED"
          },
          "callResult": {
            "type": [
              "string",
              "null"
            ],
            "description": "Call outcome when available.",
            "example": "CONNECTED"
          },
          "duration": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Call duration in seconds when available."
          },
          "transcript": {
            "type": [
              "string",
              "null"
            ],
            "description": "Call transcript when available."
          }
        }
      },
      "CallListResponse": {
        "type": "object",
        "required": [
          "calls",
          "nextCursor"
        ],
        "properties": {
          "calls": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CallListItem"
            }
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque continuation cursor, or null when no more records remain."
          }
        }
      },
      "Customer": {
        "required": [
          "id",
          "firstName",
          "lastName"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "Unique identifier for the customer. This is your customer reference and should match your internal system's ID.",
            "type": "string"
          },
          "firstName": {
            "description": "The first name of the customer",
            "type": "string"
          },
          "lastName": {
            "description": "The last name of the customer",
            "type": "string"
          },
          "email": {
            "description": "Customer email address",
            "format": "email",
            "type": [
              "string",
              "null"
            ]
          },
          "birthday": {
            "description": "Customer birthday in YYYY-MM-DD format",
            "format": "date",
            "type": [
              "string",
              "null"
            ]
          },
          "ssnLastFour": {
            "description": "Last 4 digits of SSN",
            "type": [
              "string",
              "null"
            ]
          },
          "timezone": {
            "description": "Timezone of the customer (IANA timezone name, e.g., 'America/New_York', 'America/Los_Angeles')",
            "type": [
              "string",
              "null"
            ]
          },
          "phoneNumber": {
            "description": "Phone number of the customer",
            "type": [
              "string",
              "null"
            ]
          },
          "streetAddress1": {
            "description": "Street address line 1",
            "type": [
              "string",
              "null"
            ]
          },
          "streetAddress2": {
            "description": "Street address line 2 (optional)",
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "description": "City name",
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "description": "State code (e.g., NY, CA)",
            "type": [
              "string",
              "null"
            ]
          },
          "zipCode": {
            "description": "ZIP/postal code",
            "type": [
              "string",
              "null"
            ]
          },
          "doNotCall": {
            "description": "Do not call the customer",
            "type": [
              "boolean",
              "null"
            ]
          },
          "doNotText": {
            "description": "Do not text the customer",
            "type": [
              "boolean",
              "null"
            ]
          },
          "doNotEmail": {
            "description": "Do not email the customer",
            "type": [
              "boolean",
              "null"
            ]
          },
          "bankruptcy": {
            "description": "Whether the customer has an active bankruptcy compliance status",
            "type": [
              "boolean",
              "null"
            ]
          },
          "debtConsolidation": {
            "description": "Whether the customer has an active debt-management or debt-consolidation compliance status",
            "type": [
              "boolean",
              "null"
            ]
          },
          "deceased": {
            "description": "Whether the customer has an active deceased compliance status",
            "type": [
              "boolean",
              "null"
            ]
          },
          "militaryStatus": {
            "description": "Whether the customer has an active military or servicemember compliance status",
            "type": [
              "boolean",
              "null"
            ]
          },
          "legalThreat": {
            "description": "Whether threatened legal action has been reported. This is not attorney representation and has no attorney-representation hold.",
            "type": ["boolean", "null"]
          },
          "isTest": {
            "description": "Indicates if this is a test customer",
            "type": [
              "boolean",
              "null"
            ]
          },
          "taskType": {
            "description": "Type of tasks created for the customer's automated call schedule",
            "type": [
              "string",
              "null"
            ]
          },
          "externalMetadata": {
            "description": "Additional custom metadata about the customer",
            "type": [
              "object",
              "null"
            ]
          }
        }
      },
      "NewCustomer": {
        "description": "Customer creation payload. NOTE: the required-field contract for this endpoint is changing — see the Upcoming API Changes page (/api-reference/upcoming-changes) for the new contract.",
        "required": [
          "id",
          "taskType",
          "firstName",
          "lastName",
          "phoneNumber",
          "timezone"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "Unique identifier for the customer. This is your customer reference and should match your internal system's ID.",
            "type": "string"
          },
          "firstName": {
            "description": "The first name of the customer",
            "type": "string"
          },
          "lastName": {
            "description": "The last name of the customer",
            "type": "string"
          },
          "email": {
            "description": "Customer email address. Required unless sync is true. UPCOMING: at least one of email or phoneNumber will be required — see /api-reference/upcoming-changes.",
            "type": "string",
            "format": "email",
            "example": "john.doe@example.com"
          },
          "birthday": {
            "description": "Customer birthday in YYYY-MM-DD format. UPCOMING: this field will become required — see /api-reference/upcoming-changes.",
            "type": "string",
            "format": "date",
            "example": "1985-06-15"
          },
          "ssnLastFour": {
            "description": "Last 4 digits of SSN. UPCOMING: this field will become required — see /api-reference/upcoming-changes.",
            "type": "string",
            "example": "1234"
          },
          "timezone": {
            "description": "Timezone of the customer (IANA timezone name, e.g., 'America/New_York', 'America/Los_Angeles'). Required for TCPA compliance so calls are placed during permitted hours. UPCOMING: at least one of timezone or zipCode will be required, and timezone will be derived from zipCode when omitted — see /api-reference/upcoming-changes.",
            "type": "string"
          },
          "phoneNumber": {
            "description": "Phone number of the customer. UPCOMING: at least one of email or phoneNumber will be required — see /api-reference/upcoming-changes.",
            "type": "string"
          },
          "streetAddress1": {
            "description": "Street address line 1. Required unless sync is true.",
            "type": "string",
            "example": "123 Main St"
          },
          "streetAddress2": {
            "description": "Street address line 2 (optional)",
            "type": "string",
            "example": "Apt 4B"
          },
          "city": {
            "description": "City. Required unless sync is true.",
            "type": "string",
            "example": "San Francisco"
          },
          "state": {
            "description": "State code. Required unless sync is true. UPCOMING: this field will be required in all cases — see /api-reference/upcoming-changes.",
            "type": "string",
            "example": "CA"
          },
          "zipCode": {
            "description": "ZIP/postal code. Required unless sync is true. UPCOMING: at least one of timezone or zipCode will be required — see /api-reference/upcoming-changes.",
            "type": "string",
            "example": "94102"
          },
          "doNotCall": {
            "description": "Do not call the customer",
            "type": "boolean"
          },
          "isTest": {
            "description": "Mark customer as test customer. Matches the isTest field returned on every customer response.",
            "type": "boolean",
            "default": false
          },
          "taskType": {
            "description": "Type of tasks to create for the customer's automated call schedule. Valid values depend on company configuration. This field is required.",
            "type": "string",
            "example": "collections"
          },
          "externalMetadata": {
            "description": "Additional custom metadata about the customer (e.g. employer information, pay schedule, additional contact details)",
            "type": "object",
            "properties": {
              "home_phone": {
                "type": "string",
                "example": "+14155555555"
              },
              "cell_phone": {
                "type": "string",
                "example": "+14155556666"
              },
              "employer_name": {
                "type": "string",
                "example": "Example employer"
              },
              "employer_address": {
                "type": "string",
                "example": "456 Business Ave"
              },
              "employer_city": {
                "type": "string",
                "example": "San Francisco"
              },
              "employer_state": {
                "type": "string",
                "example": "CA"
              },
              "employer_zip": {
                "type": "string",
                "example": "94103"
              },
              "employer_department": {
                "type": "string",
                "example": "Sales"
              },
              "employer_phone": {
                "type": "string",
                "example": "+14155557777"
              },
              "employer_direct_line": {
                "type": "string",
                "example": "+14155558888"
              },
              "pay_frequency": {
                "type": "string",
                "example": "biweekly"
              },
              "pay_day": {
                "type": "string",
                "example": "Friday"
              }
            }
          },
          "loanIds": {
            "description": "Array of loan IDs to import. If provided, only these loans are imported. If omitted, all loans are imported. Pass a single-element array to import exactly one loan.",
            "type": "array",
            "items": {
              "type": "integer"
            },
            "example": [
              123,
              456,
              789
            ]
          },
          "sync": {
            "type": "boolean",
            "default": false,
            "description": "When true, auto-fetch missing customer data from your integrated lending system. Defaults to false."
          },
          "loanId": {
            "type": "string",
            "description": "DEPRECATED — has no effect on this endpoint and will be removed in a future update. Pass the loan ID as a single-element loanIds array instead. See /api-reference/upcoming-changes.",
            "deprecated": true
          }
        }
      },
      "OriginationEnrollmentRequest": {
        "additionalProperties": false,
        "properties": {
          "scheduleMode": {
          "const": "enroll",
          "title": "Schedulemode",
          "type": "string"
        },
          "taskType": {
          "enum": [
            "PendingLoanNewLead",
            "PendingLoanReturning"
          ],
          "title": "Tasktype",
          "type": "string"
        },
          "id": {
          "maxLength": 255,
          "minLength": 1,
          "title": "Id",
          "type": "string"
        },
          "loanId": {
          "maxLength": 255,
          "minLength": 1,
          "title": "Loanid",
          "type": "string"
        },
          "birthday": {
          "format": "date",
          "title": "Birthday",
          "type": "string"
        },
          "ssnLastFour": {
          "pattern": "^[0-9]{4}$",
          "title": "Ssnlastfour",
          "type": "string"
        },
          "firstName": {
          "maxLength": 100,
          "minLength": 1,
          "title": "Firstname",
          "type": "string"
        },
          "lastName": {
          "maxLength": 100,
          "minLength": 1,
          "title": "Lastname",
          "type": "string"
        },
          "middleName": {
          "anyOf": [
            {
              "maxLength": 100,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Middlename"
        },
          "streetAddress1": {
          "anyOf": [
            {
              "maxLength": 255,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Streetaddress1"
        },
          "streetAddress2": {
          "anyOf": [
            {
              "maxLength": 255,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Streetaddress2"
        },
          "city": {
          "anyOf": [
            {
              "maxLength": 100,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "City"
        },
          "state": {
          "anyOf": [
            {
              "pattern": "^[A-Z]{2}$",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "State"
        },
          "zipCode": {
          "anyOf": [
            {
              "pattern": "^[0-9]{5}(?:-[0-9]{4})?$",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Zipcode"
        },
          "email": {
          "anyOf": [
            {
              "format": "email",
              "maxLength": 254,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Email"
        },
          "phoneNumber": {
          "anyOf": [
            {
              "maxLength": 32,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Phonenumber",
          "description": "Primary/home number. At least one of phoneNumber or mobilePhone is required."
        },
          "mobilePhone": {
          "anyOf": [
            {
              "maxLength": 32,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Mobilephone",
          "description": "Second mobile number, or the sole contact number when phoneNumber is absent."
        },
          "daysSincePaidOff": {
          "anyOf": [
            {
              "maximum": 36500,
              "minimum": 0,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Dayssincepaidoff",
          "description": "Selects the returning cadence by age since payoff. Returning tasks only."
        },
          "daysInApplied": {
          "anyOf": [
            {
              "maximum": 36500,
              "minimum": 0,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Daysinapplied",
          "description": "New-lead context only; does not skip scheduled attempts."
        },
          "oneOff": {
          "default": false,
          "title": "Oneoff",
          "type": "boolean",
          "description": "Returning tasks only. Exactly one scheduled call; suppresses mobile fallback."
        },
          "timezone": {
          "anyOf": [
            {
              "maxLength": 100,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Timezone",
          "description": "A valid IANA timezone, or omit when zipCode resolves to one."
        },
          "isTest": {
          "default": false,
          "description": "Mark the enrollment as a test record.",
          "title": "Is Test",
          "type": "boolean"
        },
          "sync": {
          "const": false,
          "default": false,
          "title": "Sync",
          "type": "boolean"
        },
          "doNotCall": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Donotcall"
        },
          "externalMetadata": {
          "anyOf": [
            {
              "additionalProperties": true,
              "type": "object"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Externalmetadata"
        }
        },
        "required": [
        "scheduleMode",
        "taskType",
        "id",
        "loanId",
        "birthday",
        "ssnLastFour",
        "firstName",
        "lastName"
      ],
        "title": "OriginationEnrollmentRequest",
        "type": "object",
        "description": "Application enrollment without restarting an existing cadence. A new loanId for an existing customer creates a separate loan and cadence, even if another application is pending. Existing application schedules and history are preserved; customer contact restrictions still apply. Requires scheduleMode=enroll, a new Idempotency-Key per application, and company-enabled task types. Retry the same application with its original key and unchanged payload."
      },
    "OriginationScheduleReceipt": {
      "properties": {
        "id": {
          "format": "uuid",
          "title": "Id",
          "type": "string"
        },
        "status": {
          "enum": [
            "scheduled",
            "already_scheduled"
          ],
          "title": "Status",
          "type": "string"
        },
        "customerId": {
          "title": "Customerid",
          "type": "string"
        },
        "loanId": {
          "title": "Loanid",
          "type": "string"
        },
        "scheduleId": {
          "format": "uuid",
          "title": "Scheduleid",
          "type": "string"
        },
        "scheduledCallId": {
          "format": "uuid",
          "title": "Scheduledcallid",
          "type": "string"
        },
        "scheduledTime": {
          "format": "date-time",
          "title": "Scheduledtime",
          "type": "string"
        },
        "scheduledCallCount": {
          "title": "Scheduledcallcount",
          "type": "integer"
        }
      },
      "required": [
        "id",
        "status",
        "customerId",
        "loanId",
        "scheduleId",
        "scheduledCallId",
        "scheduledTime",
        "scheduledCallCount"
      ],
      "title": "OriginationScheduleReceipt",
      "type": "object",
      "description": "Snapshot recorded at enrollment. Replays return the saved receipt, not current eligibility or call progress."
    },
    "OriginationSyncScheduleResponse": {
      "allOf": [
        {
          "$ref": "#/components/schemas/SyncScheduleResponse"
        },
        {
          "type": "object",
          "required": [
            "origination"
          ],
          "properties": {
            "origination": {
              "$ref": "#/components/schemas/OriginationScheduleReceipt"
            },
            "mobilePhone": {
              "type": [
                "string",
                "null"
              ]
            },
            "middleName": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        }
      ]
    },
    "OriginationEnrollmentError": {
      "type": "object",
      "required": [
        "detail"
      ],
      "properties": {
        "detail": {
          "type": "object",
          "required": [
            "code",
            "message"
          ],
          "properties": {
            "code": {
              "type": "string"
            },
            "message": {
              "type": "string"
            }
          }
        }
      }
    },
    "SyncCustomerSchedule": {
        "description": "Replacement-mode request. Send id and taskType. Unless sync is true, also send the core contact fields, email, and address listed below. With sync=true, a supported LMS must supply any missing core contact fields before scheduling. This contract is independent of Create Customer and origination enrollment.",
        "required": [
        "id",
        "taskType"
      ],
        "type": "object",
        "properties": {
          "id": {
          "description": "Unique identifier for the customer. This is your customer reference and should match your internal system's ID.",
          "type": "string"
        },
          "firstName": {
          "description": "The first name of the customer. Required unless sync=true supplies it from a supported LMS.",
          "type": "string"
        },
          "lastName": {
          "description": "The last name of the customer. Required unless sync=true supplies it from a supported LMS.",
          "type": "string"
        },
          "email": {
          "description": "Customer email address. Required unless sync is true.",
          "type": "string",
          "format": "email",
          "example": "john.doe@example.com"
        },
          "birthday": {
          "description": "Customer birthday in YYYY-MM-DD format",
          "type": "string",
          "format": "date",
          "example": "1985-06-15"
        },
          "ssnLastFour": {
          "description": "Last 4 digits of SSN",
          "type": "string",
          "example": "1234"
        },
          "timezone": {
          "description": "Timezone of the customer (IANA timezone name, e.g., 'America/New_York', 'America/Los_Angeles'). Required for TCPA compliance so calls are placed during permitted hours. Required unless sync=true supplies it from a supported LMS.",
          "type": "string"
        },
          "phoneNumber": {
          "description": "Phone number of the customer. Required unless sync=true supplies it from a supported LMS.",
          "type": "string"
        },
          "streetAddress1": {
          "description": "Street address line 1. Required unless sync is true.",
          "type": "string",
          "example": "123 Main St"
        },
          "streetAddress2": {
          "description": "Street address line 2 (optional)",
          "type": "string",
          "example": "Apt 4B"
        },
          "city": {
          "description": "City. Required unless sync is true.",
          "type": "string",
          "example": "San Francisco"
        },
          "state": {
          "description": "State code. Required unless sync is true.",
          "type": "string",
          "example": "CA"
        },
          "zipCode": {
          "description": "ZIP/postal code. Required unless sync is true.",
          "type": "string",
          "example": "94102"
        },
          "doNotCall": {
          "description": "Do not call the customer. If omitted, the existing value is preserved.",
          "type": "boolean"
        },
          "isTest": {
          "description": "Mark customer as test customer. Matches the isTest field returned on every customer response. When omitted, the customer's existing value is preserved.",
          "type": "boolean"
        },
          "taskType": {
          "description": "Type of tasks to create for the customer's automated call schedule. Valid values depend on company configuration.",
          "type": "string",
          "example": "Collections"
        },
          "externalMetadata": {
          "description": "Additional custom metadata about the customer (e.g. employer information, pay schedule, additional contact details)",
          "type": "object"
        },
          "sync": {
          "type": "boolean",
          "default": false,
          "description": "When true, auto-fetch missing customer data from your integrated lending system. Defaults to false."
        },
          "loanId": {
          "type": "string",
          "description": "External loan reference used by configured LMS workflows. Supplying it does not itself enable notes or other write-back."
        },
          "scheduleMode": {
          "type": "string",
          "enum": [
            "replace"
          ],
          "default": "replace",
          "description": "Omit or use replace to retain the existing customer schedule replacement behavior."
        }
        },
        "allOf": [
        {
          "if": {
            "properties": {
              "sync": {
                "const": true
              }
            },
            "required": [
              "sync"
            ]
          },
          "then": {},
          "else": {
            "required": [
              "firstName",
              "lastName",
              "phoneNumber",
              "timezone",
              "email",
              "streetAddress1",
              "city",
              "state",
              "zipCode"
            ]
          }
        }
      ]
      },
      "CustomerUpdate": {
        "description": "Partial update. Every field is optional — only the fields you send are modified; omitted fields keep their existing values.",
        "type": "object",
        "properties": {
          "firstName": {
            "description": "First name of the customer",
            "type": "string"
          },
          "lastName": {
            "description": "Last name of the customer",
            "type": "string"
          },
          "email": {
            "description": "Customer email address",
            "type": "string",
            "format": "email"
          },
          "birthday": {
            "description": "Customer birthday in YYYY-MM-DD format",
            "type": "string",
            "format": "date"
          },
          "timezone": {
            "description": "Timezone of the customer",
            "type": "string"
          },
          "phoneNumber": {
            "description": "Phone number of the customer",
            "type": "string"
          },
          "ssnLastFour": {
            "description": "Last 4 digits of SSN",
            "type": "string"
          },
          "doNotCall": {
            "description": "Do not call the customer",
            "type": "boolean"
          },
          "doNotText": {
            "description": "Do not text the customer. Setting true cancels pending SMS communication; setting false clears the channel opt-out.",
            "type": "boolean"
          },
          "doNotEmail": {
            "description": "Do not email the customer. Setting true cancels pending email communication; setting false clears the channel opt-out.",
            "type": "boolean"
          },
          "bankruptcy": {
            "description": "Set or clear the customer bankruptcy compliance status. Omitted or null preserves the current value.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "debtConsolidation": {
            "description": "Set or clear the customer debt-management or debt-consolidation status. Omitted or null preserves the current value.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "deceased": {
            "description": "Set or clear the customer deceased compliance status. Omitted or null preserves the current value.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "militaryStatus": {
            "description": "Set or clear the customer military or servicemember compliance status. Omitted or null preserves the current value.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "legalThreat": {
            "description": "Set or clear a report of threatened legal action. Omitted or null preserves the current value. This does not set attorney representation or its holds.",
            "type": ["boolean", "null"]
          },
          "state": {
            "description": "State code",
            "type": "string",
            "example": "CA"
          }
        }
      },
      "ScheduledCall": {
        "type": "object",
        "properties": {
          "id": {
            "description": "Identification number of the scheduled call",
            "type": "string"
          },
          "customerId": {
            "description": "ID of the customer",
            "type": "string"
          },
          "scheduledTime": {
            "description": "Scheduled time of the call",
            "type": "string"
          },
          "status": {
            "description": "Status of the scheduled call",
            "type": "string"
          },
          "callId": {
            "description": "Finosu call UUID, or null before a call is associated with this schedule.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "postProcessingFlags": {
            "description": "Currently always null. This endpoint exposes scheduling state, not call outcome flags. Use the configured call webhook flags field for delivered outcomes.",
            "type": "null"
          }
        },
        "required": [
          "id",
          "customerId",
          "scheduledTime",
          "status"
        ],
        "description": "Scheduled call response model matching OpenAPI spec."
      },
      "NewCall": {
        "type": "object",
        "properties": {
          "call_id": {
            "type": "string",
            "format": "uuid",
            "description": "Finosu call UUID for correlation with GET /calls/{id}. Stable across retries, including deliveries queued before deployment. Use Idempotency-Key for delivery deduplication."
          },
          "loan_id": {
            "type": "string",
            "description": "External loan reference supplied as loanId during enrollment, from the loan linked to this call's schedule. Omitted when no associated loan reference is available; never inferred from another customer loan."
          },
          "customer_id": {
            "type": "string",
            "description": "Your external customer reference ID."
          },
          "transcript": {
            "description": "Call transcript; an empty string when unavailable.",
            "type": "string"
          },
          "to_phone_number": {
            "type": "string",
            "description": "Called phone number; omitted when unavailable.",
            "example": "+14155551234"
          },
          "flags": {
            "description": "Call outcome flag codes. Omitted when there are no flags. Not postProcessingFlags. Handle unknown codes; the public task-type catalog is a subset.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "CONNECT",
              "RPC"
            ]
          },
          "summarization": {
            "description": "Summary text when summary data exists. Omitted when no summary data exists; may be an empty string when the stored summary object has no note.",
            "type": "string"
          },
          "recording_url": {
            "type": "string",
            "description": "One-hour presigned recording URL generated for each worker delivery attempt, or an empty string when unavailable. Download the recording before the link expires."
          },
          "timestamp": {
            "description": "UTC time when the worker prepares this delivery attempt, not when the call occurred. Refreshed on worker retries; immediate HTTP retries reuse the same body.",
            "type": "string",
            "format": "date-time"
          }
        },
        "description": "Outbound call-result webhook. Field names are snake_case; REST call detail uses CallResponse instead.",
        "required": [
          "call_id",
          "customer_id",
          "transcript",
          "recording_url",
          "timestamp"
        ]
      },
      "Error": {
        "type": "object",
        "required": [
          "detail"
        ],
        "description": "HTTP error envelope. Most errors contain a message; structured application errors may contain an object or array.",
        "properties": {
          "detail": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "object",
                "additionalProperties": true
              },
              {
                "type": "array",
                "items": {}
              }
            ]
          }
        }
      },
      "CustomerUpsertWebhook": {
        "type": "object",
        "required": [
          "customerId",
          "personalData"
        ],
        "properties": {
          "customerId": {
            "type": "string",
            "description": "Identifier for the customer in your system",
            "example": "customer-12345"
          },
          "personalData": {
            "type": "object",
            "required": [
              "email",
              "birthday"
            ],
            "properties": {
              "email": {
                "type": "string",
                "format": "email",
                "example": "john.doe@example.com"
              },
              "birthday": {
                "type": "string",
                "format": "date",
                "example": "1985-06-15"
              },
              "firstName": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "John"
              },
              "lastName": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "Doe"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Full name (alternative to firstName/lastName)",
                "example": "John Doe"
              },
              "phone": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "+14155551234"
              },
              "streetAddress1": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "123 Main Street"
              },
              "streetAddress2": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "Apt 4B"
              },
              "city": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "San Francisco"
              },
              "state": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "CA"
              },
              "zipCode": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "94102"
              },
              "socialSecurityNumberLastFour": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Last 4 digits of SSN",
                "example": "1234"
              },
              "countryCode": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "US"
              },
              "timezoneName": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "IANA timezone name (e.g., 'America/New_York', 'America/Los_Angeles')",
                "example": "America/Los_Angeles"
              },
              "preferredLanguage": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "ENGLISH",
                  "SPANISH",
                  null
                ],
                "example": "ENGLISH"
              },
              "address": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Full address (legacy)."
              }
            },
            "anyOf": [
              {
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              },
              {
                "required": [
                  "firstName",
                  "lastName"
                ],
                "properties": {
                  "firstName": {
                    "type": "string",
                    "minLength": 1
                  },
                  "lastName": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              }
            ]
          },
          "autodialerConsent": {
            "type": "boolean",
            "description": "Whether the customer has consented to autodialer calls",
            "example": true,
            "default": true
          },
          "externalMetadata": {
            "type": [
              "object",
              "null"
            ],
            "description": "Additional metadata about the customer",
            "properties": {
              "home_phone": {
                "type": "string",
                "example": "+14155555555"
              },
              "cell_phone": {
                "type": "string",
                "example": "+14155556666"
              },
              "employer_name": {
                "type": "string",
                "example": "Example employer"
              },
              "employer_address": {
                "type": "string",
                "example": "456 Business Ave"
              },
              "employer_city": {
                "type": "string",
                "example": "San Francisco"
              },
              "employer_state": {
                "type": "string",
                "example": "CA"
              },
              "employer_zip": {
                "type": "string",
                "example": "94103"
              },
              "employer_department": {
                "type": "string",
                "example": "Sales"
              },
              "employer_phone": {
                "type": "string",
                "example": "+14155557777"
              },
              "employer_direct_line": {
                "type": "string",
                "example": "+14155558888"
              },
              "pay_frequency": {
                "type": "string",
                "example": "biweekly"
              },
              "pay_day": {
                "type": "string",
                "example": "Monday"
              }
            }
          }
        },
        "description": "Use camelCase request fields. Legacy snake_case names remain accepted at both payload levels. CamelCase takes precedence when both spellings are supplied; duplicate personalData objects are selected, not merged. Keys inside externalMetadata are preserved as supplied."
      },
      "CustomerBatchFailure": {
        "type": "object",
        "description": "A single customer that could not be created. Successful rows are not enumerated.",
        "properties": {
          "index": { "type": "integer", "description": "0-based position of the row in the customers array you submitted, not within a server-side batch. -1 if the position could not be determined", "example": 4 },
          "customerId": { "type": "string", "description": "External customer reference ID, as submitted", "example": "CUST127" },
          "message": {
            "type": "string",
            "description": "Why the row was rejected, prefixed with the status code the equivalent single POST /customers call would have returned",
            "example": "422: Missing required fields: phoneNumber"
          }
        }
      },
      "CustomerBatchStatusResponse": {
        "type": "object",
        "properties": {
          "jobId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "completed",
              "failed",
              "cancelled"
            ],
            "description": "completed still allows individual row failures — check failures. failed means the job hit a fatal error."
          },
          "totalCustomers": {
            "type": "integer",
            "description": "Customers submitted in the batch"
          },
          "processed": {
            "type": "integer",
            "description": "Customers successfully processed"
          },
          "failed": {
            "type": "integer",
            "description": "Customers that failed"
          },
          "progressPercentage": {
            "type": "number",
            "format": "float"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "processingStartedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "processingCompletedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "errorMessage": {
            "type": [
              "string",
              "null"
            ],
            "description": "Job-level error. Null unless status is failed."
          },
          "failures": {
            "type": "array",
            "description": "Every row that could not be created, sorted by submitted index and never truncated. Partial while status is processing; final once status is completed or failed. Empty for jobs that ran before August 2026.",
            "items": {
              "$ref": "#/components/schemas/CustomerBatchFailure"
            }
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OriginationBatchResult"
            },
            "description": "Origination row outcomes in original zero-based index order. Partial until terminal; empty for legacy customer-create batches."
          }
        }
      },
      "CustomerUpsertResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "example": "success"
          },
          "customer_id": {
            "type": "string",
            "example": "customer-12345"
          },
          "created": {
            "type": "string",
            "format": "date-time",
            "example": "2025-10-31T18:30:00Z"
          },
          "updated": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "example": "2025-10-31T18:30:00Z"
          }
        },
        "required": [
          "status",
          "customer_id",
          "created",
          "updated"
        ]
      },
      "CancelCommunicationsResponse": {
        "type": "object",
        "required": [
          "id",
          "cancelledTasks",
          "cancelledCalls",
          "cancelledEmails",
          "cancelledSms",
          "cancelledCallSchedules",
          "cancelledEmailSchedules",
          "cancelledSmsSchedules",
          "totalCancelled"
        ],
        "properties": {
          "id": {
            "description": "Customer ID (reference_id)",
            "type": "string"
          },
          "cancelledTasks": {
            "description": "Always 0. Tasks are no longer cancelled directly; the field remains for compatibility.",
            "type": "integer",
            "example": 0
          },
          "cancelledCalls": {
            "description": "Number of scheduled calls cancelled",
            "type": "integer",
            "example": 3
          },
          "cancelledEmails": {
            "description": "Number of scheduled emails cancelled",
            "type": "integer",
            "example": 2
          },
          "cancelledSms": {
            "description": "Number of scheduled SMS cancelled",
            "type": "integer",
            "example": 1
          },
          "cancelledCallSchedules": {
            "description": "Number of call schedules cancelled",
            "type": "integer",
            "example": 1
          },
          "cancelledEmailSchedules": {
            "description": "Number of email schedules cancelled",
            "type": "integer",
            "example": 1
          },
          "cancelledSmsSchedules": {
            "description": "Number of SMS schedules cancelled",
            "type": "integer",
            "example": 0
          },
          "totalCancelled": {
            "description": "Total number of items cancelled",
            "type": "integer",
            "example": 13
          }
        }
      },
      "SyncScheduleResponse": {
        "type": "object",
        "required": [
          "id",
          "firstName",
          "lastName",
          "scheduleCreated",
          "tasksCreated",
          "existingTasksRemoved"
        ],
        "properties": {
          "id": {
            "description": "Customer ID (reference_id)",
            "type": "string"
          },
          "firstName": {
            "description": "Customer's first name",
            "type": "string"
          },
          "lastName": {
            "description": "Customer's last name",
            "type": "string"
          },
          "email": {
            "description": "Customer email address",
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "birthday": {
            "description": "Customer birthday in YYYY-MM-DD format",
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "ssnLastFour": {
            "description": "Last 4 digits of SSN",
            "type": [
              "string",
              "null"
            ]
          },
          "timezone": {
            "description": "Timezone of the customer (IANA timezone name, e.g., 'America/New_York', 'America/Los_Angeles')",
            "type": [
              "string",
              "null"
            ]
          },
          "phoneNumber": {
            "description": "Phone number of the customer",
            "type": [
              "string",
              "null"
            ]
          },
          "streetAddress1": {
            "description": "Street address line 1",
            "type": [
              "string",
              "null"
            ]
          },
          "streetAddress2": {
            "description": "Street address line 2 (optional)",
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "description": "City name",
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "description": "State code (e.g., NY, CA)",
            "type": [
              "string",
              "null"
            ]
          },
          "zipCode": {
            "description": "ZIP/postal code",
            "type": [
              "string",
              "null"
            ]
          },
          "doNotCall": {
            "description": "Do not call the customer",
            "type": [
              "boolean",
              "null"
            ]
          },
          "isTest": {
            "description": "Indicates if this is a test customer",
            "type": [
              "boolean",
              "null"
            ]
          },
          "taskType": {
            "description": "Type of tasks created for the customer's automated call schedule",
            "type": [
              "string",
              "null"
            ]
          },
          "externalMetadata": {
            "description": "Additional custom metadata about the customer",
            "type": [
              "object",
              "null"
            ]
          },
          "scheduleCreated": {
            "description": "Whether a new schedule was created during this sync operation",
            "type": "boolean",
            "example": true
          },
          "tasksCreated": {
            "description": "Replacement: initial task counter, generally one per created schedule, not total future calls. Enrollment: receipt scheduledCallCount when status is scheduled, otherwise 0. Replays retain original counters.",
            "type": "integer",
            "example": 1
          },
          "existingTasksRemoved": {
            "description": "Currently 0 in both modes. Replacement may still cancel schedules; this field is not a cancellation count.",
            "type": "integer",
            "example": 0
          }
        },
        "description": "Replacement mode returns saved customer data. Enrollment mode builds customer fields from the normalized request alongside a saved scheduling receipt; phoneNumber falls back to mobilePhone and doNotCall is always false. Enrollment fields are not a read of the current customer record or proof of current calling eligibility. Optional fields may be null."
      },
      "AchReturnRequest": {
        "additionalProperties": true,
        "description": "Request schema for ACH return information from external LMS.\n\nMaps to SQL query fields from external LMS system. Use camelCase fields; legacy snake_case aliases are also accepted. If both spellings are supplied, camelCase takes precedence.",
        "properties": {
          "systemSource": {
            "description": "Source system identifier",
            "type": [
              "string",
              "null"
            ]
          },
          "customerId": {
            "description": "External customer reference ID",
            "type": "string"
          },
          "loanId": {
            "description": "External loan reference ID",
            "type": "string"
          },
          "paymentId": {
            "description": "Payment processor transaction ID for the ACH payment.",
            "type": "string"
          },
          "paymentDate": {
            "description": "Date payment was made",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "paymentReverseDate": {
            "description": "Date payment was reversed",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "paymentReversed": {
            "description": "Whether payment was reversed",
            "type": [
              "boolean",
              "null"
            ]
          },
          "paymentReverseReasonCode": {
            "description": "Code for payment reversal reason",
            "type": [
              "string",
              "null"
            ]
          },
          "paymentReverseComments": {
            "description": "Comments about payment reversal",
            "type": [
              "string",
              "null"
            ]
          },
          "paymentType": {
            "description": "Type of payment",
            "type": [
              "string",
              "null"
            ]
          },
          "paymentAmount": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Payment amount"
          },
          "achSubmittedDate": {
            "description": "Date ACH was submitted",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "achSettlementDate": {
            "description": "Date ACH settled",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "achStatus": {
            "type": [
              "string",
              "number",
              "null"
            ],
            "description": "External ACH status. Numeric values and numeric strings are normalized to integer strings. Status mapping depends on the integration; this is not a closed enum."
          },
          "achReturnCode": {
            "description": "ACH return code",
            "type": [
              "string",
              "null"
            ]
          },
          "achReturnDate": {
            "description": "Date of ACH return",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "achReturnReason": {
            "description": "Reason for ACH return",
            "type": [
              "string",
              "null"
            ]
          },
          "achCorrectionCode": {
            "description": "ACH correction code from NOC",
            "type": [
              "string",
              "null"
            ]
          },
          "achCorrectedAbaNumber": {
            "description": "Corrected ABA routing number",
            "type": [
              "string",
              "null"
            ]
          },
          "achCorrectedAccountNumber": {
            "description": "Corrected account number",
            "type": [
              "string",
              "null"
            ]
          },
          "achCorrectionDate": {
            "description": "Date correction was received",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "debitStatus": {
            "description": "Debit card transaction status",
            "type": [
              "string",
              "null"
            ]
          },
          "debitDeclineReason": {
            "description": "Reason for debit card decline",
            "type": [
              "string",
              "null"
            ]
          },
          "overallReturnCode": {
            "description": "Combined return code",
            "type": [
              "string",
              "null"
            ]
          },
          "isMetadataError": {
            "description": "Set to true to indicate this is a permanent metadata error that should deactivate the payment method",
            "type": "boolean",
            "default": false
          }
        },
        "required": [
          "customerId",
          "loanId",
          "paymentId"
        ],
        "type": "object"
      },
      "CancelLoanServicingRequest": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "Reason for stopping servicing. Retrieve current accepted values with GET /loans/stop-servicing-reasons. Defaults to API_REQUEST if not specified.",
            "enum": [
              "PAID_OFF",
              "BANKRUPTCY",
              "FRAUD_CLAIM",
              "MILITARY",
              "DECEASED",
              "INCARCERATED",
              "DEBT_MANAGEMENT_COMPANY",
              "NO_LOGIN_2M",
              "NO_PAYMENT_OR_PLAN_3M",
              "NO_PLAN_PAYMENT_2M",
              "NO_PAYMENT_OR_PLAN_4M",
              "SOLD_OFF",
              "MISTAKE",
              "DEBT_DISPUTE",
              "COMPLAINT_TRIGGER",
              "HISTORICAL_INELIGIBLE",
              "DUPLICATE",
              "REFUSES_TO_PAY",
              "API_REQUEST",
              "LMS_CLOSED",
              "DATA_DEFECT_HOLD",
              "COERCED_DEBT_CLAIM",
              "TRANSFERRED_TO_FINOSU"
            ],
            "example": "PAID_OFF"
          },
          "note": {
            "type": ["string", "null"],
            "minLength": 1,
            "maxLength": 2000,
            "description": "Optional free-form audit note (1–2000 characters after trimming). Common financial-secret and contact-detail patterns are redacted before storage. Does not change the selected reason or invoke a flag workflow.",
            "example": "Servicing recalled at the lender's request."
          }
        }
      },
      "CancelLoanServicingResponse": {
        "type": "object",
        "required": [
          "id",
          "servicingStatus",
          "stopServicingReason",
          "cancelledScheduledPayments",
          "cancelledCallSchedules",
          "cancelledEmailSchedules",
          "cancelledSmsSchedules",
          "cancelledCalls",
          "cancelledEmails",
          "cancelledSms"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Loan external reference ID"
          },
          "servicingStatus": {
            "type": "string",
            "description": "New servicing status (NOT_SERVICING)",
            "example": "NOT_SERVICING"
          },
          "stopServicingReason": {
            "type": "string",
            "description": "Reason for stopping servicing",
            "example": "PAID_OFF"
          },
          "cancelledScheduledPayments": {
            "type": "integer",
            "description": "Number of scheduled payments cancelled",
            "example": 3
          },
          "cancelledCallSchedules": {
            "type": "integer",
            "description": "Number of call schedules cancelled",
            "example": 1
          },
          "cancelledEmailSchedules": {
            "type": "integer",
            "description": "Number of email schedules cancelled",
            "example": 1
          },
          "cancelledSmsSchedules": {
            "type": "integer",
            "description": "Number of SMS schedules cancelled",
            "example": 1
          },
          "cancelledCalls": {
            "type": "integer",
            "description": "Number of scheduled calls cancelled",
            "example": 5
          },
          "cancelledEmails": {
            "type": "integer",
            "description": "Number of scheduled emails cancelled",
            "example": 2
          },
          "cancelledSms": {
            "type": "integer",
            "description": "Number of scheduled SMS cancelled",
            "example": 3
          }
        }
      },
      "CancelCustomerServicingResponse": {
        "type": "object",
        "required": [
          "id",
          "loansUpdated",
          "stopServicingReason",
          "cancelledScheduledPayments",
          "cancelledCallSchedules",
          "cancelledEmailSchedules",
          "cancelledSmsSchedules",
          "cancelledCalls",
          "cancelledEmails",
          "cancelledSms"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Customer ID (reference_id)"
          },
          "loansUpdated": {
            "type": "integer",
            "description": "Number of loans set to NOT_SERVICING",
            "example": 3
          },
          "stopServicingReason": {
            "type": "string",
            "description": "Reason for stopping servicing",
            "example": "PAID_OFF"
          },
          "cancelledScheduledPayments": {
            "type": "integer",
            "description": "Total scheduled payments cancelled across all loans",
            "example": 9
          },
          "cancelledCallSchedules": {
            "type": "integer",
            "description": "Total call schedules cancelled",
            "example": 3
          },
          "cancelledEmailSchedules": {
            "type": "integer",
            "description": "Total email schedules cancelled",
            "example": 3
          },
          "cancelledSmsSchedules": {
            "type": "integer",
            "description": "Total SMS schedules cancelled",
            "example": 3
          },
          "cancelledCalls": {
            "type": "integer",
            "description": "Total scheduled calls cancelled",
            "example": 15
          },
          "cancelledEmails": {
            "type": "integer",
            "description": "Total scheduled emails cancelled",
            "example": 6
          },
          "cancelledSms": {
            "type": "integer",
            "description": "Total scheduled SMS cancelled",
            "example": 9
          }
        }
      },
      "NewLoan": {
        "required": ["id", "customerId", "amountDue", "dueDate", "servicingStatus", "totalBalance", "originationDate"],
        "type": "object",
        "properties": {
          "id": { "description": "Your stable loan reference ID", "type": "string", "example": "LOAN-12345" },
          "customerId": { "description": "External reference of a previously-pushed customer", "type": "string", "example": "CUST-001" },
          "type": { "type": "string", "enum": ["CASH_ADVANCE", "PERSONAL_LOAN", "INSTALLMENT_LOAN"] },
          "applicationStatus": { "type": "string", "enum": ["PENDING", "APPROVED", "FUNDED", "WITHDRAWN", "REJECTED"] },
          "originalFundedAmount": { "type": "number", "minimum": 0, "example": 500.00 },
          "originalTotalOwed": { "type": "number", "minimum": 0, "example": 650.00 },
          "interestRate": { "description": "Non-negative decimal where 1.0 = 100% (e.g. 6.49 = 649%)", "type": "number", "minimum": 0, "example": 0.2999 },
          "apr": { "description": "Non-negative decimal where 1.0 = 100%", "type": "number", "minimum": 0, "example": 0.3550 },
          "originationDate": { "description": "Required. Date the loan was originated.", "type": "string", "format": "date", "example": "2026-04-01" },
          "startDate": { "type": "string", "format": "date" },
          "totalBalance": { "description": "Required. Current outstanding balance.", "type": "number", "minimum": 0, "example": 425.00 },
          "payoffAmount": { "type": "number", "minimum": 0 },
          "totalPayoffAmount": { "type": "number", "minimum": 0 },
          "balanceAtTransfer": { "type": "number", "minimum": 0 },
          "preTransferPayments": { "type": "number", "minimum": 0 },
          "amountDue": { "description": "Next scheduled installment amount. Charged-off loans with no scheduled installment may send 0 when chargeoffDate and chargeoffAmount are provided. Must be non-negative and is immutable after creation. A zero amount does not create an initial scheduled-payment obligation.", "type": "number", "minimum": 0, "example": 125.00 },
          "dueDate": { "description": "Next scheduled installment date. For a charged-off loan with amountDue 0, send the last missed contractual due date, or chargeoffDate when that date is unavailable. Do not send the borrower's next payroll date.", "type": "string", "format": "date", "example": "2026-05-01" },
          "servicingStatus": { "description": "Must be ACTIVE on create. Use cancel-servicing endpoint to stop.", "type": "string", "enum": ["ACTIVE"] },
          "isSettled": { "type": "boolean" },
          "chargeoffDate": { "description": "Must be paired with chargeoffAmount", "type": "string", "format": "date" },
          "chargeoffAmount": { "description": "Must be paired with chargeoffDate", "type": "number", "minimum": 0 },
          "autopayEnabled": { "type": "boolean", "example": true },
          "noCallReason": { "type": "string", "enum": ["CUSTOMER_OPT_OUT", "LEGAL_HOLD", "BANKRUPT", "INCARCERATED", "DECEASED", "MILITARY", "FRAUD", "AGENCY_REQUEST", "COMPLAINT", "WRONG_NUMBER", "OTHER"] },
          "noTextReason": { "type": "string", "enum": ["CUSTOMER_OPT_OUT", "LEGAL_HOLD", "BANKRUPT", "INCARCERATED", "DECEASED", "MILITARY", "FRAUD", "AGENCY_REQUEST", "COMPLAINT", "WRONG_NUMBER", "OTHER"] },
          "noEmailReason": { "type": "string", "enum": ["CUSTOMER_OPT_OUT", "LEGAL_HOLD", "BANKRUPT", "INCARCERATED", "DECEASED", "MILITARY", "FRAUD", "AGENCY_REQUEST", "COMPLAINT", "WRONG_NUMBER", "OTHER"] },
          "isVisibleInBorrowerPortal": { "type": "boolean", "example": true },
          "daysPastDueAtBoarding": { "type": "integer", "minimum": 0 },
          "daysSinceOriginationAtBoarding": { "type": "integer", "minimum": 0 },
          "externalLmsMetadata": { "type": "object" },
          "portfolioId": { "description": "Portfolio UUID for payment processor routing", "type": "string", "format": "uuid" },
          "paymentPlanMinPercentage": { "description": "Min settlement percentage (0-1). Min and max must be supplied together. When both are omitted, the loan is pay-in-full unless an approved lender-specific creation policy derives a band.", "type": "number", "minimum": 0, "maximum": 1 },
          "paymentPlanMaxPercentage": { "description": "Max settlement percentage (0-1). Min and max must be supplied together. When both are omitted, the loan is pay-in-full unless an approved lender-specific creation policy derives a band.", "type": "number", "minimum": 0, "maximum": 1 },
          "taskType": { "description": "Schedule task type (e.g. Collections). Defaults to Collections when omitted.", "type": "string", "example": "Collections" }
        }
      },
      "UpdateLoan": {
        "type": "object",
        "description": "Partial update. id, customerId, amountDue cannot be changed. Spark/Kwai collection-balance requests contain only current_balance (outstanding including accrued interest). No interest, timestamp, update ID or category. Payment/plan activity freezes uploads. Receipts cannot prove source freshness; see Update Loan for ordering, resume gates and rollout requirements.",
        "properties": {
          "current_balance": { "type": ["number", "string"], "description": "Spark/Kwai collection-only outstanding USD balance including accrued interest. Send this field alone. Nonnegative, finite, at most 99999999.99, at cent precision. No source timestamp or replay identity is provided." },
          "type": { "type": "string", "enum": ["CASH_ADVANCE", "PERSONAL_LOAN", "INSTALLMENT_LOAN"] },
          "applicationStatus": { "type": "string", "enum": ["PENDING", "APPROVED", "FUNDED", "WITHDRAWN", "REJECTED"] },
          "originalFundedAmount": { "type": "number" },
          "originalTotalOwed": { "type": "number" },
          "interestRate": { "type": "number" },
          "apr": { "type": "number" },
          "originationDate": { "type": "string", "format": "date" },
          "startDate": { "type": "string", "format": "date" },
          "totalBalance": { "type": "number" },
          "payoffAmount": { "type": "number" },
          "totalPayoffAmount": { "type": "number" },
          "balanceAtTransfer": { "type": "number" },
          "preTransferPayments": { "type": "number" },
          "dueDate": { "type": "string", "format": "date" },
          "servicingStatus": { "type": "string", "enum": ["ACTIVE", "NOT_SERVICING"] },
          "stopServicingReason": { "type": "string" },
          "isSettled": { "type": "boolean" },
          "chargeoffDate": { "type": "string", "format": "date" },
          "chargeoffAmount": { "type": "number" },
          "autopayEnabled": { "type": "boolean" },
          "noCallReason": { "type": "string" },
          "noTextReason": { "type": "string" },
          "noEmailReason": { "type": "string" },
          "isVisibleInBorrowerPortal": { "type": "boolean" },
          "daysPastDueAtBoarding": { "type": "integer" },
          "daysSinceOriginationAtBoarding": { "type": "integer" },
          "externalLmsMetadata": { "type": "object" }
          ,"fraud": { "description": "Set or clear the fraud claim on this exact loan. Omitted or null makes no change. The fraud cascade owns servicing and channel-block fields.", "type": ["boolean", "null"] }
        }
      },
      "UpdateACHAuthorization": {
        "type": "object",
        "description": "Partial revocation update for an exact ACH payment method.",
        "properties": {
          "revokeAuthorization": {
            "description": "true revokes the active authorization. false is a no-op only while authorization remains active; it cannot restore revoked consent. Omitted or null makes no change.",
            "type": ["boolean", "null"]
          }
        }
      },
      "PublicPaymentMethodResponse": {
        "type": "object",
        "required": [
          "paymentMethodId",
          "paymentMethodType",
          "lastFour",
          "accountType",
          "status",
          "isDefault"
        ],
        "properties": {
          "paymentMethodId": {
            "type": "string",
            "format": "uuid",
            "description": "Finosu payment-method UUID; use for ACH revocation of bank accounts."
          },
          "paymentMethodType": {
            "type": "string",
            "enum": [
              "CREDIT_CARD",
              "DEBIT_CARD",
              "BANK_ACCOUNT",
              "CARD",
              "DIGITAL_WALLET"
            ]
          },
          "lastFour": {
            "type": "string",
            "description": "Masked account/card suffix; not a unique account identifier."
          },
          "accountType": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "INACTIVE",
              "EXPIRED",
              "FAILED",
              "PENDING_VERIFICATION"
            ]
          },
          "isDefault": {
            "type": "boolean"
          }
        }
      },
      "ACHAuthorizationStatus": {
        "type": "object",
        "required": ["customerId", "paymentMethodId", "revokeAuthorization"],
        "properties": {
          "customerId": { "type": "string" },
          "paymentMethodId": { "type": "string", "format": "uuid" },
          "revokeAuthorization": { "type": "boolean" }
        }
      },
      "Loan": {
        "type": "object",
        "properties": {
          "current_balance": { "type": ["string", "null"], "description": "Latest accepted raw Spark balance; local payments are deducted separately. Internal totalBalance is unchanged." },
          "id": { "type": "string", "description": "External loan reference ID" },
          "customerId": { "type": "string", "description": "External customer reference ID" },
          "type": {
            "type": [
              "string",
              "null"
            ]
          },
          "applicationStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "originalFundedAmount": {
            "type": [
              "string",
              "null"
            ],
            "description": "Decimal value serialized as a JSON string; null when unavailable."
          },
          "originalTotalOwed": {
            "type": [
              "string",
              "null"
            ],
            "description": "Decimal value serialized as a JSON string; null when unavailable."
          },
          "interestRate": {
            "type": [
              "string",
              "null"
            ],
            "description": "Decimal value serialized as a JSON string; null when unavailable."
          },
          "apr": {
            "type": [
              "string",
              "null"
            ],
            "description": "Decimal value serialized as a JSON string; null when unavailable."
          },
          "originationDate": {
            "format": "date",
            "type": [
              "string",
              "null"
            ]
          },
          "startDate": {
            "format": "date",
            "type": [
              "string",
              "null"
            ]
          },
          "totalBalance": {
            "type": [
              "string",
              "null"
            ],
            "description": "Decimal value serialized as a JSON string; null when unavailable."
          },
          "payoffAmount": {
            "type": [
              "string",
              "null"
            ],
            "description": "Decimal value serialized as a JSON string; null when unavailable."
          },
          "totalPayoffAmount": {
            "type": [
              "string",
              "null"
            ],
            "description": "Decimal value serialized as a JSON string; null when unavailable."
          },
          "balanceAtTransfer": {
            "type": [
              "string",
              "null"
            ],
            "description": "Decimal value serialized as a JSON string; null when unavailable."
          },
          "preTransferPayments": {
            "type": [
              "string",
              "null"
            ],
            "description": "Decimal value serialized as a JSON string; null when unavailable."
          },
          "amountDue": {
            "type": "string",
            "description": "Decimal value serialized as a JSON string."
          },
          "dueDate": { "type": "string" },
          "servicingStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "stopServicingReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "isSettled": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "chargeoffDate": {
            "type": [
              "string",
              "null"
            ]
          },
          "chargeoffAmount": {
            "type": [
              "string",
              "null"
            ],
            "description": "Decimal value serialized as a JSON string; null when unavailable."
          },
          "autopayEnabled": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "noCallReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "noTextReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "noEmailReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "isVisibleInBorrowerPortal": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "daysPastDueAtBoarding": {
            "type": [
              "integer",
              "null"
            ]
          },
          "daysSinceOriginationAtBoarding": {
            "type": [
              "integer",
              "null"
            ]
          },
          "externalLmsMetadata": {
            "type": [
              "object",
              "null"
            ]
          },
          "portfolioId": {
            "format": "uuid",
            "type": [
              "string",
              "null"
            ]
          },
          "paymentPlanMinPercentage": {
            "type": [
              "string",
              "null"
            ],
            "description": "Decimal value serialized as a JSON string; null when unavailable."
          },
          "paymentPlanMaxPercentage": {
            "type": [
              "string",
              "null"
            ],
            "description": "Decimal value serialized as a JSON string; null when unavailable."
          },
          "taskType": {
            "type": [
              "string",
              "null"
            ]
          },
          "fraud": { "description": "Whether this exact loan has a fraud claim", "type": "boolean" }
        },
        "required": [
          "id",
          "customerId",
          "amountDue",
          "dueDate"
        ]
      },
      "LoanBatchResponse": {
        "type": "object",
        "properties": {
          "jobId": { "type": "string", "format": "uuid", "description": "Job ID. Use with GET /loans/batch/{jobId}", "example": "550e8400-e29b-41d4-a716-446655440000" },
          "totalLoans": { "type": "integer", "description": "Number of loans queued for processing", "example": 400 },
          "message": { "type": "string", "description": "How many parallel slices the job was split into, and how to poll it", "example": "Batch job created with 16 parallel batches. Use GET /loans/batch/550e8400-e29b-41d4-a716-446655440000 to check status." }
        }
      },
      "LoanBatchFailure": {
        "type": "object",
        "description": "A single loan that could not be written. Successful rows are not enumerated.",
        "properties": {
          "index": { "type": "integer", "description": "0-based position of the row in the loans array you submitted, not within a server-side slice", "example": 7 },
          "id": { "type": "string", "description": "External loan reference ID, as submitted", "example": "LOAN-12345" },
          "customerId": { "type": "string", "description": "External customer reference ID, as submitted", "example": "CUST-001" },
          "message": { "type": "string", "description": "Why the row was rejected, prefixed with the status code the equivalent single POST /loans call would have returned", "example": "404: Customer with ID CUST-999 not found" }
        }
      },
      "LoanBatchStatusResponse": {
        "type": "object",
        "properties": {
          "jobId": { "type": "string", "format": "uuid" },
          "status": { "type": "string", "enum": ["pending", "processing", "completed", "failed"], "description": "completed still allows individual row failures — check failures. failed means every row failed or the job hit a fatal error." },
          "totalLoans": { "type": "integer", "description": "Loans submitted in the batch" },
          "processed": { "type": "integer", "description": "Loans successfully processed" },
          "failed": { "type": "integer", "description": "Loans that failed" },
          "progressPercentage": { "type": "number", "format": "float" },
          "createdAt": { "type": "string", "format": "date-time" },
          "processingStartedAt": { "type": "string", "format": "date-time", "nullable": true },
          "processingCompletedAt": { "type": "string", "format": "date-time", "nullable": true },
          "errorMessage": { "type": "string", "nullable": true, "description": "Job-level error. Null unless status is failed." },
          "failures": { "type": "array", "description": "Every row that could not be written, sorted by submitted index and never truncated. Partial while status is processing; final once status is completed or failed.", "items": { "$ref": "#/components/schemas/LoanBatchFailure" } }
        }
      },
      "DeleteLoanResponse": {
        "type": "object",
        "required": ["id", "deleted", "servicingStatus", "cancelledScheduledPayments", "cancelledCalls", "cancelledEmails", "cancelledSms"],
        "properties": {
          "id": { "type": "string" },
          "deleted": { "type": "boolean", "example": true },
          "servicingStatus": { "type": "string", "example": "NOT_SERVICING" },
          "cancelledScheduledPayments": { "type": "integer", "example": 2 },
          "cancelledCalls": { "type": "integer", "example": 3 },
          "cancelledEmails": { "type": "integer", "example": 1 },
          "cancelledSms": { "type": "integer", "example": 1 }
        }
      },
      "LoanLevelSyncResult": {
        "type": "object",
        "properties": {
          "finosuLoanId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Finosu internal loan UUID"
          },
          "loanId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Loan identifier in the external LMS (echoes the loanId from the request)"
          }
        }
      },
      "LmsLoanSyncResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "lmsType": {
            "type": "string",
            "description": "Identifier of the configured LMS platform"
          },
          "customerId": {
            "type": "string",
            "description": "External LMS customer identifier discovered from the loan"
          },
          "finosuCustomerId": {
            "type": "string",
            "format": "uuid",
            "description": "Finosu internal customer UUID"
          },
          "loansSynced": {
            "type": "integer"
          },
          "loans": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LoanLevelSyncResult"
            }
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "success",
          "lmsType",
          "customerId",
          "finosuCustomerId",
          "loansSynced"
        ]
      },
      "LmsLoanSyncRequest": {
        "type": "object",
        "required": [
          "loanId"
        ],
        "properties": {
          "loanId": {
            "type": "string",
            "minLength": 1,
            "description": "Loan identifier in the external LMS",
            "example": "99001"
          }
        }
      },
      "LmsWebhookSyncRequest": {
        "type": "object",
        "required": [
          "customerId"
        ],
        "properties": {
          "customerId": {
            "type": "string",
            "minLength": 1,
            "description": "Customer identifier in the external LMS (e.g., CIF number for NLS)",
            "example": "12345"
          }
        }
      },
      "LmsWebhookSyncResponse": {
        "type": "object",
        "required": [
          "success",
          "lmsType",
          "customerId",
          "finosuCustomerId",
          "loansSynced"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether the sync completed successfully"
          },
          "lmsType": {
            "type": "string",
            "description": "The LMS type used (e.g., 'nls')",
            "example": "nls"
          },
          "customerId": {
            "type": "string",
            "description": "External LMS customer identifier",
            "example": "12345"
          },
          "finosuCustomerId": {
            "type": "string",
            "format": "uuid",
            "description": "Finosu internal customer UUID",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "loansSynced": {
            "type": "integer",
            "description": "Number of loans synced",
            "example": 2
          },
          "loans": {
            "type": "array",
            "description": "Detailed results for each loan",
            "items": {
              "$ref": "#/components/schemas/LoanSyncResult"
            }
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Error message if sync failed"
          }
        }
      },
      "LoanSyncResult": {
        "type": "object",
        "properties": {
          "loanId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Finosu internal loan UUID (null if loan creation failed)",
            "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
          },
          "nortridgeAcctrefno": {
            "type": [
              "integer",
              "null"
            ],
            "description": "External servicing account reference number",
            "example": 100001
          },
          "loanExternalReference": {
            "type": [
              "string",
              "null"
            ],
            "description": "External loan reference",
            "example": "LOAN-001"
          },
          "transactionsRows": {
            "type": "integer",
            "description": "Number of transactions synced",
            "example": 15
          },
          "paymentsDueRows": {
            "type": "integer",
            "description": "Number of payments due synced",
            "example": 12
          },
          "paymentMethodsCreated": {
            "type": "integer",
            "description": "Number of payment methods created",
            "example": 1
          },
          "paymentMethodsUpdated": {
            "type": "integer",
            "description": "Number of payment methods updated",
            "example": 0
          },
          "documentRows": {
            "type": "integer",
            "description": "Number of documents synced",
            "example": 3
          },
          "transactionsError": {
            "type": [
              "string",
              "null"
            ],
            "description": "Error syncing transactions (if any)"
          },
          "paymentsDueError": {
            "type": [
              "string",
              "null"
            ],
            "description": "Error syncing payments due (if any)"
          },
          "paymentMethodsError": {
            "type": [
              "string",
              "null"
            ],
            "description": "Error syncing payment methods (if any)"
          },
          "documentsError": {
            "type": [
              "string",
              "null"
            ],
            "description": "Error syncing documents (if any)"
          },
          "setupError": {
            "type": [
              "string",
              "null"
            ],
            "description": "Error during loan setup (if any)"
          }
        }
      },
      "TransferNote": {
        "type": "object",
        "required": [
          "customer_id",
          "summary",
          "answers"
        ],
        "properties": {
          "customer_id": {
            "type": "string",
            "description": "Unique identifier for the customer",
            "example": "customer-12345"
          },
          "summary": {
            "type": "string",
            "description": "AI-generated summary of the call including verification status, employment details, banking information, and other key information gathered during the conversation",
            "example": "In call, person was verified as Mark Scout. Mark Scout works for Example employer as a Macrodata Refinement Engineer. Mark Scout has a direct deposit and a checking account. Mark Scout is employed full time and has a recent direct deposit. Mark Scout is not active military. Mark Scout is not receiving financial assistance. Mark Scout is paid monthly. His payment frequency is weekly. Mark Scout is not on any specific day of the week. Mark Scout does not have a do not call before date. Mark Scout is not spamming text messages. Mark Scout has no additional notes."
          },
          "answers": {
            "type": "object",
            "description": "Free-form object containing all collected information from the call. Common fields include contact information, employment details, banking information, verification status, and preferences. Fields may vary based on the conversation flow.",
            "properties": {
              "date_of_birth": {
                "type": "string",
                "format": "date",
                "description": "Customer's date of birth in YYYY-MM-DD format",
                "example": "1990-01-01"
              },
              "state": {
                "type": "string",
                "description": "Two-letter state code",
                "example": "CO"
              },
              "personal_phone_1": {
                "type": "string",
                "description": "Primary phone number",
                "example": "+16508629110"
              },
              "personal_phone_1_contact": {
                "type": "string",
                "description": "Whether contact was made on this number",
                "example": "Yes"
              },
              "employer_phone": {
                "type": "string",
                "description": "Employer's phone number",
                "example": "+16508629114"
              },
              "speaking_to_customer": {
                "type": "string",
                "description": "Whether speaking directly to the customer",
                "example": "Yes"
              },
              "active_military": {
                "type": "string",
                "description": "Active military status",
                "example": "No"
              },
              "currently_employed": {
                "type": "string",
                "description": "Current employment status",
                "example": "Employed"
              },
              "company_name": {
                "type": "string",
                "description": "Name of employer",
                "example": "Example employer"
              },
              "home_address_1": {
                "type": "string",
                "description": "Street address",
                "example": "123 Main St"
              },
              "home_city": {
                "type": "string",
                "description": "City",
                "example": "Denver"
              },
              "home_state": {
                "type": "string",
                "description": "State code",
                "example": "CO"
              },
              "zipcode": {
                "type": "string",
                "description": "ZIP code",
                "example": "80201"
              },
              "direct_deposit": {
                "type": "string",
                "description": "Whether customer has direct deposit",
                "example": "Yes"
              },
              "checking_account": {
                "type": "string",
                "description": "Checking account status",
                "example": "Direct Deposit"
              }
            },
            "additionalProperties": true
          }
        },
        "description": "Integration-specific outbound transfer note. Configured separately from the call-result webhook."
      },
      "HTTPError": {
        "type": "object",
        "required": [
          "detail"
        ],
        "properties": {
          "detail": {
            "type": "string"
          }
        }
      },
      "ValidationError": {
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "properties": {
          "loc": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            }
          },
          "msg": {
            "type": "string"
          },
          "type": {
            "type": "string"
          }
        }
      },
      "ValidationErrorResponse": {
        "type": "object",
        "required": [
          "detail"
        ],
        "properties": {
          "detail": {
            "description": "Application validation returns a message; framework validation returns an array of field errors.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            ]
          }
        }
      },
      "EnrollmentValidationErrorResponse": {
        "anyOf": [
          {
            "$ref": "#/components/schemas/ValidationErrorResponse"
          },
          {
            "$ref": "#/components/schemas/OriginationEnrollmentError"
          }
        ]
      },
      "CallResponse": {
        "properties": {
          "customerId": {
            "type": "string",
            "description": "Your external customer reference, falling back to the Finosu customer UUID when no reference is stored."
          },
          "toPhoneNumber": {
            "description": "Called phone number, or null when unavailable.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "transcript": {
            "description": "Call transcript, or null when unavailable.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "recordingUrl": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Fresh one-hour recording URL, or null when unavailable."
          },
          "timestamp": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Call record creation time in ISO 8601 format, or null."
          }
        },
        "type": "object",
        "required": [
          "customerId"
        ],
        "description": "REST call detail returned by GET /calls/{id} and GET /calls/customer/{customerId}. This is not the outbound call webhook payload."
      },
      "TextResponse": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string"
          },
          "content": {
            "type": [
              "string",
              "null"
            ]
          },
          "timestamp": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "customerId"
        ]
      },
      "EmailResponse": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string"
          },
          "subject": {
            "type": [
              "string",
              "null"
            ]
          },
          "content": {
            "type": [
              "string",
              "null"
            ]
          },
          "timestamp": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "customerId"
        ]
      },
      "PublicExternalPaymentRequest": {
        "description": "A successful payment already settled outside Finosu. CamelCase is preferred; matching snake_case aliases are accepted. Extra fields are rejected.",
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "loanId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Exact company-owned external loan/account identifier."
          },
          "transactionId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Stable lender transaction identity, unique across the authenticated company."
          },
          "sourceSystem": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50,
            "pattern": "^[a-z0-9][a-z0-9._-]*$",
            "description": "Stable lowercase originating LMS identifier. Only authenticated Kwai/Spark requests may omit it, in which case Finosu uses sparklms. All other companies must supply it. Explicit existing sources are preserved and must remain unchanged on retries; null is rejected.",
            "example": "example-lms"
          },
          "amount": {
            "type": "string",
            "format": "decimal",
            "pattern": "^(?!0+(?:\\.0{1,2})?$)\\d{1,8}(?:\\.\\d{1,2})?$",
            "description": "Positive USD amount no greater than 21474836.47, with at most two decimal places."
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD"
            ]
          },
          "paymentDate": {
            "type": "string",
            "format": "date",
            "description": "Settlement date in YYYY-MM-DD format."
          },
          "status": {
            "type": "string",
            "enum": [
              "settled"
            ]
          },
          "settlesLoan": {
            "type": "boolean",
            "default": false,
            "description": "True only for a lender-finalized full payoff or negotiated settlement."
          }
        },
        "required": [
          "loanId",
          "transactionId",
          "amount",
          "currency",
          "paymentDate",
          "status"
        ]
      },
      "PublicExternalPaymentResponse": {
        "type": "object",
        "properties": {
          "paymentTransactionId": {
            "type": "string",
            "format": "uuid"
          },
          "transactionId": {
            "type": "string"
          },
          "loanId": {
            "type": "string"
          },
          "amount": {
            "type": "string",
            "format": "decimal"
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD"
            ]
          },
          "paymentDate": {
            "type": "string",
            "format": "date"
          },
          "status": {
            "type": "string",
            "enum": [
              "recorded",
              "duplicate"
            ]
          },
          "replayed": {
            "type": "boolean"
          },
          "overpayment": {
            "type": "boolean"
          },
          "settledLoan": {
            "type": "boolean"
          }
        },
        "required": [
          "paymentTransactionId",
          "transactionId",
          "loanId",
          "amount",
          "currency",
          "paymentDate",
          "status",
          "replayed",
          "overpayment",
          "settledLoan"
        ]
      },
      "AchConfirmRequest": {
        "description": "Request schema for ACH settlement confirmation from external LMS.\n\nMaps to the same ID conventions as ACH returns. Use camelCase fields; legacy snake_case aliases are also accepted. If both spellings are supplied, camelCase takes precedence.",
        "properties": {
          "settlementDate": {
            "description": "Date the ACH payment settled",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "customerId": {
            "description": "External customer reference ID",
            "type": "string"
          },
          "loanId": {
            "description": "External loan reference ID",
            "type": "string"
          },
          "paymentId": {
            "description": "Payment processor transaction ID for the ACH payment.",
            "type": "string"
          }
        },
        "required": [
          "customerId",
          "loanId",
          "paymentId"
        ],
        "type": "object"
      },
      "AchConfirmBatchRequest": {
        "description": "Batch request schema for multiple ACH confirmations from external LMS.\nAccepts an array of ACH confirm records for efficient bulk processing. Use camelCase fields; legacy snake_case aliases are also accepted. If both spellings are supplied, camelCase takes precedence.",
        "properties": {
          "confirms": {
            "description": "Array of ACH confirm records to process",
            "items": {
              "$ref": "#/components/schemas/AchConfirmRequest"
            },
            "type": "array"
          }
        },
        "required": [
          "confirms"
        ],
        "type": "object"
      },
      "AchConfirmItemResponse": {
        "description": "Response for individual ACH confirm in a batch.",
        "properties": {
          "success": {
            "description": "Whether this individual confirm was processed successfully",
            "type": "boolean"
          },
          "payment_id": {
            "description": "Payment processor transaction ID for the ACH payment.",
            "type": "string"
          },
          "message": {
            "description": "Success or error message for this confirm",
            "type": "string"
          }
        },
        "required": [
          "success",
          "payment_id",
          "message"
        ],
        "type": "object"
      },
      "AchConfirmBatchResponse": {
        "description": "Response schema for batch ACH confirm submission.",
        "properties": {
          "success": {
            "description": "Whether all confirms were processed successfully",
            "type": "boolean"
          },
          "total_processed": {
            "description": "Total number of confirms processed",
            "type": "integer"
          },
          "successful": {
            "description": "Number of successfully processed confirms",
            "type": "integer"
          },
          "failed": {
            "description": "Number of failed confirms",
            "type": "integer"
          },
          "results": {
            "description": "Individual results for each confirm",
            "items": {
              "$ref": "#/components/schemas/AchConfirmItemResponse"
            },
            "type": "array"
          }
        },
        "required": [
          "success",
          "total_processed",
          "successful",
          "failed",
          "results"
        ],
        "type": "object"
      },
      "AchConfirmResponse": {
        "description": "Response schema for ACH settlement confirmation.",
        "properties": {
          "success": {
            "description": "Whether the confirmation was successful",
            "type": "boolean"
          },
          "message": {
            "description": "Success or error message",
            "type": "string"
          }
        },
        "required": [
          "success",
          "message"
        ],
        "type": "object"
      },
      "AchReturnBatchRequest": {
        "description": "Batch request schema for multiple ACH returns from external LMS.\nAccepts an array of ACH return records for efficient bulk processing. Use camelCase fields; legacy snake_case aliases are also accepted. If both spellings are supplied, camelCase takes precedence.",
        "properties": {
          "returns": {
            "description": "Array of ACH return records to process",
            "items": {
              "$ref": "#/components/schemas/AchReturnRequest"
            },
            "type": "array"
          }
        },
        "required": [
          "returns"
        ],
        "type": "object"
      },
      "AchReturnItemResponse": {
        "description": "Response for individual ACH return in a batch.",
        "properties": {
          "success": {
            "description": "Whether this individual return was processed successfully",
            "type": "boolean"
          },
          "payment_id": {
            "description": "Payment processor transaction ID for the ACH payment.",
            "type": "string"
          },
          "message": {
            "description": "Success or error message for this return",
            "type": "string"
          },
          "payment_method_disabled": {
            "description": "Whether processing disabled the payment method; null when not applicable.",
            "type": [
              "boolean",
              "null"
            ]
          }
        },
        "required": [
          "success",
          "payment_id",
          "message"
        ],
        "type": "object"
      },
      "AchReturnBatchResponse": {
        "description": "Response schema for batch ACH return submission.",
        "properties": {
          "success": {
            "description": "Whether all returns were processed successfully",
            "type": "boolean"
          },
          "total_processed": {
            "description": "Total number of returns processed",
            "type": "integer"
          },
          "successful": {
            "description": "Number of successfully processed returns",
            "type": "integer"
          },
          "failed": {
            "description": "Number of failed returns",
            "type": "integer"
          },
          "results": {
            "description": "Individual results for each return",
            "items": {
              "$ref": "#/components/schemas/AchReturnItemResponse"
            },
            "type": "array"
          }
        },
        "required": [
          "success",
          "total_processed",
          "successful",
          "failed",
          "results"
        ],
        "type": "object"
      },
      "AchReturnResponse": {
        "description": "Response schema for ACH return information submission.",
        "properties": {
          "success": {
            "description": "Whether the submission was successful",
            "type": "boolean"
          },
          "message": {
            "description": "Success or error message",
            "type": "string"
          },
          "payment_method_disabled": {
            "description": "Whether processing disabled the payment method; null when not applicable.",
            "type": [
              "boolean",
              "null"
            ]
          }
        },
        "required": [
          "success",
          "message"
        ],
        "type": "object"
      },
      "LmsBatchSyncRequest": {
        "type": "object",
        "properties": {
          "customerIds": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "pattern": "\\S"
            },
            "minItems": 1,
            "maxItems": 100,
            "description": "External customer identifiers; each must contain a non-whitespace character."
          }
        },
        "required": [
          "customerIds"
        ]
      },
      "LmsBatchSyncResponse": {
        "type": "object",
        "properties": {
          "jobId": {
            "type": "string",
            "description": "Job identifier for status polling."
          },
          "totalCustomers": {
            "type": "integer"
          },
          "message": {
            "type": "string"
          }
        },
        "required": [
          "jobId",
          "totalCustomers",
          "message"
        ]
      },
      "LmsBatchStatusResponse": {
        "type": "object",
        "properties": {
          "jobId": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "totalCustomers": {
            "type": "integer"
          },
          "processed": {
            "type": "integer"
          },
          "failed": {
            "type": "integer"
          },
          "progressPercentage": {
            "type": "number"
          },
          "createdAt": {
            "type": "string"
          },
          "processingStartedAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "processingCompletedAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "errorMessage": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "jobId",
          "status",
          "totalCustomers",
          "processed",
          "failed",
          "progressPercentage",
          "createdAt"
        ]
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      },
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key"
      }
    }
  }
}
