openapi: 3.1.0 info: title: Pioneer Loan Platform API version: 1.0.0 description: |- # Pioneer Public API The Pioneer API allows third-party systems to programmatically access loan data, documents, and related entities. ## URL Structure API requests use environment-specific domains (following existing portal pattern): ``` Production: https://{api-portal}.{bank-domain}/api/public/v1/{resource} Staging: https://{api-portal}.staging.{bank-domain}/api/public/v1/{resource} ``` - **Production**: `https://api.pioneerft.com/api/public/v1/...` (use `pk_live_*` keys) - **Staging**: `https://api.staging.pioneerft.com/api/public/v1/...` (use `pk_test_*` keys) ## Versioning The current version is **v1**, served under the `/api/public/v1` path prefix. The legacy unversioned path `/api/public/...` still works but is **deprecated**: responses include `Deprecation: true` and a `Sunset` header. Migrate to `/api/public/v1`. ## Authentication All API requests require authentication using an API key. Include your API key in the `Authorization` header: ``` Authorization: Bearer pk_live_your_api_key_here ``` **Key Types:** - `pk_test_*` - Test keys (only work on staging URLs) - `pk_live_*` - Live keys (only work on production URLs - no env prefix) API keys can be generated in the Pioneer dashboard under Organization Settings. Note: Production keys require explicit approval from Pioneer. ## Rate Limiting Rate limits are applied per API key, with separate budgets for reads and writes: - **Reads** (GET): **100 requests per minute** per API key. - **Writes** (POST/PATCH/DELETE): **30 requests per minute** per API key. Rate limit information is included in response headers: - `X-RateLimit-Limit`: Maximum requests per window - `X-RateLimit-Remaining`: Requests remaining in current window - `X-RateLimit-Reset`: Unix timestamp when window resets When rate limited, you'll receive a `429 Too Many Requests` response with a `Retry-After` header indicating seconds to wait. **⚠️ Important:** Production API keys (`pk_live_*`) that exceed rate limits 3 or more times within 24 hours will be automatically revoked, and your organization's production API access will be disabled. Contact Pioneer support to re-enable access. ## Idempotency Write requests (POST/PATCH/DELETE) accept an optional `Idempotency-Key` header (any opaque string up to 255 chars; a UUID is recommended). Replaying a request with the same key returns the original response instead of repeating the side effect. ## Pagination List endpoints support pagination via query parameters: - `limit`: Number of items to return (default: 50, max: 100) - `offset`: Number of items to skip Response includes pagination metadata. Offset-mode responses look like: ```json { "data": [], "meta": { "mode": "offset", "limit": 50, "offset": 0, "hasMore": true } } ``` Cursor-mode responses (returned when a `cursor` query param is supplied) instead contain `{ "mode": "cursor", "limit": 50, "hasMore": true, "nextCursor": "" }`, where `nextCursor` is an opaque token (null when there are no more results). Pass it back as the `cursor` query param to fetch the next page. Offset and cursor are mutually exclusive, and cursor mode always orders by creation time. ## Errors Errors are returned with appropriate HTTP status codes and JSON body: ```json { "code": "auth.invalid_key", "message": "Invalid or expired API key" } ``` Common error codes: - `auth.invalid_key`: API key is invalid or expired - `auth.org_mismatch`: API key does not belong to the organization in the URL - `auth.env_mismatch`: API key type does not match URL environment - `auth.env_not_available`: Environment not available for this organization - `auth.insufficient_permissions`: API key lacks required permission - `auth.country_restricted`: Request IP not allowed for this API key - `api_key.production_not_allowed`: Organization not approved for production API keys - `input.invalid`: Request validation failed - `rate_limit.exceeded`: Too many requests ## OpenAPI document The spec is published in two OpenAPI versions, both under _Download OpenAPI description_ and each in YAML and JSON. They are generated from the same routes in the same run, describe the identical API, and differ only in schema dialect. **OpenAPI 3.1.0** is the current one. It uses JSON Schema 2020-12: `type: [..., "null"]` for nullables and numeric `exclusiveMinimum`/`exclusiveMaximum`. **OpenAPI 3.0.3** is for tooling that does not read 3.1 — the same document with `nullable: true` and boolean `exclusive*` instead. It is also what the Postman collection is generated from. 3.0.3 is the last patch of the 3.0 line; a consumer pinned to 3.0.0, 3.0.1 or 3.0.2 reads it without changes, since those patches clarify the specification rather than alter the document format. If your generator supports both, take 3.1.0. ## API clients Ready-to-import clients are generated from these same routes in the same run, so they never drift from the live API. Both are listed under _Download OpenAPI description_ above, next to the spec itself. **Postman.** Import the collection plus the environment for your target (Staging or Production), then set `apiKey` to your key — every request inherits collection-level bearer auth. Path parameters such as `:id` appear as editable fields under the URL bar, and optional query parameters are pre-filled but disabled, so tick only the ones you need. **Insomnia.** Imports OpenAPI directly: **Import** -> **File** -> choose the OpenAPI 3.0.3 YAML. Set the base environment's `base_url` to the environment you are targeting and add your key as a bearer token. Need access or a key? [Contact us](https://www.pioneerft.com). contact: name: Pioneer Support email: contact@pioneerft.com url: https://www.pioneerft.com license: name: Proprietary url: https://pioneerft.com/terms servers: - url: https://api.pioneerft.com/api description: Production (pk_live_* keys) - url: https://api.staging.pioneerft.com/api description: Staging (pk_test_* keys) security: - ApiKeyAuth: [] tags: - name: Loans description: Access and update loan data including details, summaries, status, and entities - name: Tasks description: Read a loan’s workflow approval tasks - name: Decisions description: Read per-product underwriting decisions for a loan - name: Disbursements description: List and manage loan disbursements (amounts in whole cents) - name: Collateral description: List and manage loan collateral (cents for money, basis points for rates) - name: Debt description: Read a loan’s debt schedule (read-only) - name: Entities description: Read, create, and update the businesses and people on a loan. `GET /loans/{id}/entities` lists them; the per-entity GETs add full addresses and contact details. Tax identifiers are masked to the last 4 digits unless the key carries `read:businesses:ein` / `read:people:ssn`. - name: Documents description: List, upload, download, update, and delete loan documents - name: Products description: List available loan products - name: Webhooks description: Manage outbound webhook endpoints and inspect delivery attempts paths: /public/v1/loans: get: operationId: listLoans tags: - Loans summary: List loans description: Retrieve a paginated list of loans. Supports filtering by status and product ID. Returns loan summary information without sensitive details. responses: "200": description: Successful response with list of loans content: application/json: schema: type: object properties: data: type: array items: $ref: "#/components/schemas/PublicLoan" meta: $ref: "#/components/schemas/PaginationMeta" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: query name: limit schema: default: 50 type: number minimum: 1 maximum: 100 - in: query name: offset schema: type: number minimum: 0 - in: query name: cursor schema: type: string - in: query name: status schema: type: string enum: - draft - application - active - denied - withdrawn - funded - in: query name: productId schema: type: string - in: query name: sort schema: default: createdAt type: string enum: - createdAt - centsAmount - status - loanNumber - in: query name: order schema: default: desc type: string enum: - asc - desc /public/v1/loans/boarding-queue: get: operationId: listBoardingQueue tags: - Loans summary: List loans ready to board description: "Loans a banker released to the core and nobody has boarded yet, oldest first. A loan reaches this queue only once a banker releases it — being funded is not enough on its own. Acknowledge a board with `POST /loans/{id}/boarding` and the loan leaves the queue, so a scheduled pull only ever returns work that is still outstanding: a board that failed on your side simply stays. Pass `boarded=true` (optionally with `boardedSince`) to reconcile instead — the loans already boarded." parameters: - in: query name: limit schema: default: 50 type: number minimum: 1 maximum: 100 - in: query name: offset schema: type: number minimum: 0 - in: query name: cursor schema: type: string - in: query name: boarded schema: default: "false" type: string enum: - "true" - "false" - in: query name: boardedSince schema: type: string responses: "200": description: Loans ready to board (or already boarded) content: application/json: schema: type: object properties: data: type: array items: allOf: - $ref: "#/components/schemas/PublicLoan" - type: object properties: boarding: $ref: "#/components/schemas/PublicLoanBoarding" meta: $ref: "#/components/schemas/PaginationMeta" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 /public/v1/loans/{id}: get: operationId: getLoan tags: - Loans summary: Get loan details description: Retrieve detailed information about a specific loan by ID. Returns loan terms, amounts, dates, and status without exposing sensitive internal data. responses: "200": description: Successful response with loan details content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicLoan" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true patch: operationId: updateLoan tags: - Loans summary: Update a loan description: "Update loan fields. Money is in whole cents (`centsAmount: 50000000` is $500,000.00) and rates in basis points (`bpsFixedRate: 525` is 5.25%). Only allowlisted fields can be updated — server-managed fields like status and stage are excluded." responses: "200": description: Loan updated successfully content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicLoan" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true requestBody: required: true content: application/json: schema: type: object properties: centsAmount: type: integer exclusiveMinimum: 0 maximum: 9007199254740991 bpsFixedRate: type: integer minimum: 0 maximum: 9007199254740991 bpsSpreadRate: type: integer minimum: 0 maximum: 9007199254740991 bpsPrimeRate: type: integer minimum: 0 maximum: 9007199254740991 rateType: type: string enum: - fixed - variable monthsTerm: type: integer exclusiveMinimum: 0 maximum: 9007199254740991 monthsInterestOnly: type: integer minimum: 0 maximum: 9007199254740991 paymentFrequency: type: string enum: - weekly - biweekly - monthly - quarterly - semi-annual - annual - lump sum - irregular payments purpose: type: string purposeNarrative: type: string type: type: string enum: - business - personal loanNumber: type: string productId: type: string applicationDate: type: string pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d))|(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?)$ closeDate: type: string pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d))|(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?)$ firstPaymentDate: type: string pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d))|(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?)$ delete: operationId: deleteLoan tags: - Loans summary: Archive a loan description: Soft-delete a loan by setting its deletion timestamp. The loan will no longer appear in list results but can be restored by Pioneer support. responses: "200": description: Loan archived successfully content: application/json: schema: type: object properties: message: type: string example: Loan archived "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true /public/v1/loans/{id}/summary: get: operationId: getLoanSummary tags: - Loans summary: Get loan summary description: Retrieve a summary of a loan including key metrics like total amount, monthly payment, term, and activity status (open tasks, message activity). `hasMessages` reflects non-internal (customer/broker-visible) conversation only — internal banker threads are never exposed. responses: "200": description: Successful response with loan summary content: application/json: schema: type: object properties: data: type: object properties: loan: $ref: "#/components/schemas/PublicLoan" metrics: type: object properties: centsTotalAmount: type: integer centsMonthlyPayment: type: integer monthsTerm: type: integer hasOpenTasks: type: boolean hasMessages: type: boolean "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true /public/v1/loans/{id}/entities: get: operationId: getLoanEntities tags: - Loans summary: Get loan entities description: Retrieve businesses and people associated with a loan. Returns entity names, types, and roles. Sensitive data like full SSN/EIN is excluded (only last 4 digits shown). responses: "200": description: Successful response with loan entities content: application/json: schema: type: object properties: data: type: object properties: businesses: type: array items: type: object properties: id: type: string name: type: string dba: type: string entityType: type: string einLast4: type: string state: type: string role: type: string people: type: array items: type: object properties: id: type: string firstName: type: string lastName: type: string ssnLast4: type: string role: type: string ownershipPercent: type: number "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true /public/v1/loans/search: post: operationId: searchLoans tags: - Loans summary: Search loans description: Advanced loan search with complex filtering capabilities. Supports multiple status filters, date ranges, amount ranges (in whole cents), and custom sorting. Use this endpoint when you need more control than the basic list endpoint. requestBody: description: Search criteria content: application/json: schema: type: object properties: filters: type: object properties: status: type: array items: type: string enum: - draft - application - active - denied - withdrawn - funded productIds: type: array items: type: string centsAmountMin: type: integer minimum: -9007199254740991 maximum: 9007199254740991 centsAmountMax: type: integer minimum: -9007199254740991 maximum: 9007199254740991 dateRange: type: object properties: from: type: string to: type: string pagination: type: object properties: limit: default: 50 type: number minimum: 1 maximum: 100 offset: type: number minimum: 0 cursor: type: string sort: type: object properties: field: default: createdAt type: string enum: - createdAt - centsAmount - status - loanNumber order: default: desc type: string enum: - asc - desc required: true responses: "200": description: Successful response with search results content: application/json: schema: type: object properties: data: type: array items: $ref: "#/components/schemas/PublicLoan" meta: $ref: "#/components/schemas/PaginationMeta" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 /public/v1/loans/{id}/tasks: get: operationId: listLoanTasks tags: - Tasks summary: List loan tasks description: "List the workflow tasks (submit / pre-approve / approve / re-submit steps) for a loan, with open/completed status. Read-only: tasks are completed automatically by the loan workflow/decision engine when the corresponding action occurs, not via a direct API write." responses: "200": description: Loan tasks content: application/json: schema: type: object properties: data: type: array items: $ref: "#/components/schemas/PublicTask" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true /public/v1/loans/{id}/decisions: get: operationId: listLoanDecisions tags: - Decisions summary: List underwriting decisions description: List the per-product underwriting decisions for a loan. Returns the verdict (approved/declined) and, for declines, the reason. Raw rule internals and applicant data are never exposed. responses: "200": description: Loan underwriting decisions content: application/json: schema: type: object properties: data: type: array items: $ref: "#/components/schemas/PublicDecision" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true /public/v1/loans/{id}/status: get: operationId: getLoanStatus tags: - Loans summary: Get loan status description: Get a loan's current status and stage. responses: "200": description: Current loan status content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicLoanStatus" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true /public/v1/loans/{id}/timeline: get: operationId: getLoanTimeline tags: - Loans summary: Get loan status timeline description: Get the loan's status-transition history (most recent first), sourced from the audit log. responses: "200": description: Loan status timeline content: application/json: schema: type: object properties: data: type: array items: $ref: "#/components/schemas/PublicTimelineEvent" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true /public/v1/loans/{id}/boarding: get: operationId: getLoanBoarding tags: - Loans summary: Get a loan's boarding state description: Whether a banker released the loan to the core, who released it and when, and whether the core has acknowledged the board. responses: "200": description: Boarding state content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicLoanBoarding" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true post: operationId: acknowledgeLoanBoarding tags: - Loans summary: Acknowledge a board description: "Record that the core boarded the loan. The loan leaves the boarding queue and the event is audited. Only a loan a banker released can be acknowledged — a loan that never reached the queue returns 400. Safe to repeat: a second call returns the boarding that already stands rather than overwriting it, so a retry after a lost response cannot rewrite when the core boarded the loan. Send `coreRecordNumber` to store the core’s own identifier for the loan on the Pioneer record." parameters: - in: header name: Idempotency-Key required: false schema: type: string maxLength: 255 description: Optional opaque key (e.g. a UUID) to make this write safely retryable. A replay with the same key returns the original response instead of repeating the side effect. - in: path name: id schema: type: string required: true responses: "200": description: Boarding recorded (or already recorded) content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicLoanBoarding" "400": description: The loan was never released to the core, so it is not in the queue content: application/json: schema: type: object properties: code: type: string example: loan_not_released_for_boarding message: type: string example: This loan has not been released to the core yet, so it is not in the boarding queue. "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 requestBody: required: true content: application/json: schema: type: object properties: coreRecordNumber: type: string minLength: 1 maxLength: 255 /public/v1/loans/{id}/messages: get: operationId: listLoanMessages tags: - Messages summary: List loan messages description: Retrieve messages posted on a loan across its customer/broker-visible chatrooms. Internal banker-only threads are never returned. responses: "200": description: Loan messages (most recent first) content: application/json: schema: type: object properties: data: type: array items: $ref: "#/components/schemas/PublicMessage" meta: $ref: "#/components/schemas/PaginationMeta" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true - in: query name: limit schema: default: 50 type: number minimum: 1 maximum: 100 - in: query name: offset schema: type: number minimum: 0 - in: query name: cursor schema: type: string /public/v1/loans/{id}/disbursements: get: operationId: listLoanDisbursements tags: - Disbursements summary: List loan disbursements description: Retrieve the disbursements recorded against a loan, including amounts (in whole cents), recipients, payment methods, and status. responses: "200": description: Loan disbursements content: application/json: schema: type: object properties: data: type: array items: $ref: "#/components/schemas/PublicDisbursement" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true post: operationId: createDisbursement tags: - Disbursements summary: Create a disbursement description: Create a disbursement on a loan. `centsAmount` is in whole cents — 50000000 is $500,000.00. parameters: - in: header name: Idempotency-Key required: false schema: type: string maxLength: 255 description: Optional opaque key (e.g. a UUID) to make this write safely retryable. A replay with the same key returns the original response instead of repeating the side effect. - in: path name: id schema: type: string required: true responses: "201": description: Disbursement created content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicDisbursement" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 requestBody: required: true content: application/json: schema: type: object properties: glAccountId: description: Deprecated — accepted for backward compatibility and ignored. type: string category: type: string enum: - borrower_proceeds - refinance - equipment_purchase - real_estate_purchase - payroll - broker_fee - sba_guarantee_fee - third_party_payment - other centsAmount: type: integer exclusiveMinimum: 0 maximum: 9007199254740991 description: Amount in whole cents. 50000000 = $500,000.00 recipientName: type: string minLength: 1 recipientType: type: string enum: - borrower - vendor - broker - lender - other paymentMethod: type: string enum: - wire - ach - check - internal_transfer notes: type: string required: - category - centsAmount - recipientName - recipientType - paymentMethod /public/v1/gl-accounts: get: operationId: listGlAccounts tags: - Disbursements summary: List GL accounts description: List the organization’s general-ledger accounts. Returns active accounts by default. responses: "200": description: GL accounts content: application/json: schema: type: object properties: data: type: array items: $ref: "#/components/schemas/PublicGLAccount" meta: $ref: "#/components/schemas/PaginationMeta" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: query name: limit schema: default: 50 type: number minimum: 1 maximum: 100 - in: query name: offset schema: default: 0 type: number minimum: 0 - in: query name: search schema: type: string minLength: 1 - in: query name: includeInactive schema: default: false type: boolean /public/v1/loans/{loanId}/disbursements/{id}: patch: operationId: updateDisbursement tags: - Disbursements summary: Update a disbursement description: Update an existing disbursement on a loan. Only the fields provided in the request body are changed; `centsAmount` is in whole cents. parameters: - in: header name: Idempotency-Key required: false schema: type: string maxLength: 255 description: Optional opaque key (e.g. a UUID) to make this write safely retryable. A replay with the same key returns the original response instead of repeating the side effect. - in: path name: loanId schema: type: string required: true - in: path name: id schema: type: string required: true responses: "200": description: Disbursement updated content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicDisbursement" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 requestBody: required: true content: application/json: schema: type: object properties: category: type: string enum: - borrower_proceeds - refinance - equipment_purchase - real_estate_purchase - payroll - broker_fee - sba_guarantee_fee - third_party_payment - other centsAmount: type: integer exclusiveMinimum: 0 maximum: 9007199254740991 description: Amount in whole cents. 50000000 = $500,000.00 recipientName: type: string minLength: 1 recipientType: type: string enum: - borrower - vendor - broker - lender - other paymentMethod: type: string enum: - wire - ach - check - internal_transfer status: type: string enum: - pending - approved - processing - completed - failed - cancelled notes: type: string /public/v1/loans/{id}/collateral: get: operationId: listLoanCollateral tags: - Collateral summary: List loan collateral description: Retrieve the collateral items securing a loan, including descriptions, values (in whole cents), advance rate (in basis points), and types. responses: "200": description: Loan collateral content: application/json: schema: type: object properties: data: type: array items: $ref: "#/components/schemas/PublicCollateral" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true post: operationId: createCollateral tags: - Collateral summary: Add collateral to a loan description: Add collateral to a loan. Money is in whole cents and `bpsAdvanceRate` is in basis points (8500 = 85%). parameters: - in: header name: Idempotency-Key required: false schema: type: string maxLength: 255 description: Optional opaque key (e.g. a UUID) to make this write safely retryable. A replay with the same key returns the original response instead of repeating the side effect. - in: path name: id schema: type: string required: true responses: "201": description: Collateral created content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicCollateral" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 requestBody: required: true content: application/json: schema: type: object properties: description: type: string category: type: string centsMarketValue: type: integer minimum: 0 maximum: 9007199254740991 description: Market value in whole cents. 12345600 = $123,456.00 bpsAdvanceRate: type: integer minimum: 0 maximum: 10000 description: Advance rate in basis points, 0–10000 (8500 = 85%). Not inferred — supply it to drive net-collateral/LTV. centsManualPriorLien: type: integer minimum: 0 maximum: 9007199254740991 description: Manually-entered prior lien in whole cents (used in net-collateral/LTV). ownerType: type: string enum: - person - business ownerId: type: string /public/v1/loans/{loanId}/collateral/{id}: patch: operationId: updateCollateral tags: - Collateral summary: Update collateral description: Update collateral. Money is in whole cents and `bpsAdvanceRate` is in basis points (8500 = 85%). parameters: - in: header name: Idempotency-Key required: false schema: type: string maxLength: 255 description: Optional opaque key (e.g. a UUID) to make this write safely retryable. A replay with the same key returns the original response instead of repeating the side effect. - in: path name: loanId schema: type: string required: true - in: path name: id schema: type: string required: true responses: "200": description: Collateral updated content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicCollateral" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 requestBody: required: true content: application/json: schema: type: object properties: description: type: string category: type: string centsMarketValue: type: integer minimum: 0 maximum: 9007199254740991 description: Market value in whole cents. 12345600 = $123,456.00 bpsAdvanceRate: type: integer minimum: 0 maximum: 10000 description: Advance rate in basis points, 0–10000 (8500 = 85%). Not inferred — supply it to drive net-collateral/LTV. centsManualPriorLien: type: integer minimum: 0 maximum: 9007199254740991 description: Manually-entered prior lien in whole cents (used in net-collateral/LTV). ownerType: type: string enum: - person - business ownerId: type: string /public/v1/loans/{id}/debt: get: operationId: listLoanDebt tags: - Debt summary: List loan debt schedule description: List a loan's debt schedule (read-only). Debt records are synced from the loan's components/underwriting data and are not editable row-by-row via the API; money is in whole cents and `bpsInterestRate` is in basis points. responses: "200": description: Loan debt schedule content: application/json: schema: type: object properties: data: type: array items: $ref: "#/components/schemas/PublicDebt" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true /public/v1/businesses/{id}: get: operationId: getBusiness tags: - Entities summary: Get a business description: Retrieve full details for a business, including its addresses. The EIN is returned masked (`einLast4`) unless the API key also holds the `read:businesses:ein` scope, in which case the full value is returned in `ein`. A null `ein` with a non-null `einLast4` means the key lacks that scope; both null means no EIN is on record. responses: "200": description: Successful response with business detail content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicBusinessDetail" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true /public/v1/people/{id}: get: operationId: getPerson tags: - Entities summary: Get a person description: Retrieve full details for a person, including their addresses. The SSN is returned masked (`ssnLast4`) unless the API key also holds the `read:people:ssn` scope, in which case the full value is returned in `ssn`. responses: "200": description: Successful response with person detail content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicPersonDetail" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true /public/v1/loans/{id}/businesses: post: operationId: createLoanBusiness tags: - Entities summary: Create a business on a loan description: Create a business and attach it to the loan with the given role (e.g. "borrower", "guarantor"). Accepts a full EIN; only the last 4 digits are ever returned. parameters: - in: header name: Idempotency-Key required: false schema: type: string maxLength: 255 description: Optional opaque key (e.g. a UUID) to make this write safely retryable. A replay with the same key returns the original response instead of repeating the side effect. - in: path name: id schema: type: string required: true responses: "201": description: Business created content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicBusiness" "400": description: Invalid role or input "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 requestBody: required: true content: application/json: schema: type: object properties: name: type: string minLength: 1 ein: type: string dba: type: string entityType: type: string enum: - C-Corp - S-Corp - General Partnership - Limited Liability Company - Limited Liability Partnership - Sole Proprietorship - Trust state: type: string naics: type: string maxLength: 6 role: type: string minLength: 1 description: Entity-in-loan role name, e.g. "borrower" or "guarantor" (resolved server-side; unknown names return 400). See the roles echoed by GET /loans/:id/entities. required: - name - role /public/v1/loans/{loanId}/businesses/{id}: patch: operationId: updateLoanBusiness tags: - Entities summary: Update a business on a loan description: Update the details of a business associated with a loan. Only the fields provided in the request body are changed. parameters: - in: header name: Idempotency-Key required: false schema: type: string maxLength: 255 description: Optional opaque key (e.g. a UUID) to make this write safely retryable. A replay with the same key returns the original response instead of repeating the side effect. - in: path name: loanId schema: type: string required: true - in: path name: id schema: type: string required: true responses: "200": description: Business updated content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicBusiness" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 requestBody: required: true content: application/json: schema: type: object properties: name: type: string minLength: 1 ein: type: string dba: type: string entityType: type: string enum: - C-Corp - S-Corp - General Partnership - Limited Liability Company - Limited Liability Partnership - Sole Proprietorship - Trust state: type: string naics: type: string maxLength: 6 /public/v1/loans/{id}/people: post: operationId: createLoanPerson tags: - Entities summary: Create a person on a loan description: Create a person and attach them to the loan with the given role. Accepts a full SSN; only the last 4 digits are ever returned. parameters: - in: header name: Idempotency-Key required: false schema: type: string maxLength: 255 description: Optional opaque key (e.g. a UUID) to make this write safely retryable. A replay with the same key returns the original response instead of repeating the side effect. - in: path name: id schema: type: string required: true responses: "201": description: Person created content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicPerson" "400": description: Invalid role or input "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 requestBody: required: true content: application/json: schema: type: object properties: firstName: type: string minLength: 1 lastName: type: string minLength: 1 ssn: type: string email: type: string format: email pattern: ^(?:[A-Za-z0-9_'+\-]+\.)*[A-Za-z0-9_'+\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$ dob: type: string pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d))|(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?)$ role: type: string minLength: 1 description: Entity-in-loan role name, e.g. "borrower" or "guarantor" (resolved server-side; unknown names return 400). See the roles echoed by GET /loans/:id/entities. required: - firstName - lastName - role /public/v1/loans/{loanId}/people/{id}: patch: operationId: updateLoanPerson tags: - Entities summary: Update a person on a loan description: Update the details of a person associated with a loan. Only the fields provided in the request body are changed. parameters: - in: header name: Idempotency-Key required: false schema: type: string maxLength: 255 description: Optional opaque key (e.g. a UUID) to make this write safely retryable. A replay with the same key returns the original response instead of repeating the side effect. - in: path name: loanId schema: type: string required: true - in: path name: id schema: type: string required: true responses: "200": description: Person updated content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicPerson" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 requestBody: required: true content: application/json: schema: type: object properties: firstName: type: string minLength: 1 lastName: type: string minLength: 1 ssn: type: string email: type: string format: email pattern: ^(?:[A-Za-z0-9_'+\-]+\.)*[A-Za-z0-9_'+\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$ dob: type: string pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d))|(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?)$ /public/v1/loans/{id}/documents: get: operationId: listLoanDocuments tags: - Documents summary: List loan documents description: "Retrieve every document on a loan: files uploaded against a document requirement (the rows on the loan's Documents tab, whether the requirement targets the loan, a person, or a business) plus loan-level artifacts the platform generates, such as term sheets and adverse-action notices. Each entry carries the `requirement` it satisfies — including its review `status` and its `id`, which is what you pass as `requirementID` when uploading. `requirement` is null for generated artifacts. Use the download endpoint to get a presigned URL for the file itself." responses: "200": description: Successful response with document list content: application/json: schema: type: object properties: data: type: array items: $ref: "#/components/schemas/PublicDocument" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true post: operationId: uploadDocument tags: - Documents summary: Upload a document description: >- Step 1 of 2. Creates the document record and returns a presigned upload URL; PUT the file content to that URL, then call `POST /documents/{id}/confirm` — the confirm step is what verifies the bytes landed and attaches the file to its requirement. A document that is never confirmed stays invisible to the bank. Pass `requirementID` (a requirement `id` from `GET /loans/{id}/documents`) so the upload satisfies that requirement and appears on the banker's Documents tab for review. Omit it only for a loan-level attachment that answers no requirement — those are not surfaced for review. parameters: - in: header name: Idempotency-Key required: false schema: type: string maxLength: 255 description: Optional opaque key (e.g. a UUID) to make this upload safely retryable. A replay with the same key returns the original response instead of creating a duplicate record. - in: path name: id schema: type: string required: true responses: "201": description: Document record created with presigned upload URL content: application/json: schema: type: object properties: data: type: object properties: id: type: string uploadUrl: type: string format: uri expiresAt: type: string format: date-time "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 requestBody: required: true content: application/json: schema: type: object properties: name: type: string minLength: 1 maxLength: 255 type: type: string minLength: 1 size: type: integer exclusiveMinimum: 0 maximum: 9007199254740991 requirementID: type: string required: - name - type - size /public/v1/documents/{id}/download: get: operationId: getDocumentDownloadUrl tags: - Documents summary: Get document download URL description: Get a presigned download URL for a document. The URL is valid for 5 minutes. Use this URL to download the actual document file directly. responses: "200": description: Successful response with download URL content: application/json: schema: type: object properties: data: type: object properties: id: type: string name: type: string type: type: string size: type: integer category: type: string uploadedAt: type: string format: date-time downloadUrl: type: string format: uri expiresAt: type: string format: date-time "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true /public/v1/products: get: operationId: listProducts tags: - Products summary: List products description: Retrieve a list of available loan products. Returns product details including name, description, terms, and rate types. responses: "200": description: Successful response with product list content: application/json: schema: type: object properties: data: type: array items: $ref: "#/components/schemas/PublicProduct" "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 /public/v1/products/{id}: get: operationId: getProduct tags: - Products summary: Get product details description: Retrieve detailed information about a specific loan product by ID. Returns product name, description, terms, amortization period, rate type, and payment frequency. responses: "200": description: Successful response with product details content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicProduct" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true /public/v1/documents/{id}/confirm: post: operationId: confirmDocumentUpload tags: - Documents summary: Confirm a document upload description: >- Step 2 of 2. Call after PUTting the file content to the presigned URL. Verifies the blob actually arrived (and is within the size cap) and, for a requirement-backed upload, attaches it to that requirement so it appears on the banker's Documents tab as `PENDING` review. Until this is called the record exists but the bank cannot see the document. **Pass `period` when the requirement collects more than one** (three years of tax returns, say) — those 400 with `period_key_required` otherwise, since nothing can infer which year the file answers. Use the `period` value from `GET /loans/{id}/documents`. Retrying is safe in the sense that it never corrupts anything, but it is NOT a no-op: re-confirming an already-attached file adds another version to the same requirement, matching how a re-upload behaves in the UI. Retry after a *failed* confirm, not after a successful one. parameters: - in: header name: Idempotency-Key required: false schema: type: string maxLength: 255 description: Optional opaque key (e.g. a UUID) to make this write safely retryable. A replay with the same key returns the original response instead of repeating the side effect. - in: path name: id schema: type: string required: true responses: "200": description: Upload confirmed and attached content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicDocument" "400": description: Blob missing, oversized, or the requirement cannot accept an upload "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 requestBody: required: true content: application/json: schema: type: object properties: period: type: string description: Which period this document answers, e.g. `2024`. REQUIRED when the requirement collects more than one period (three years of tax returns, say); those 400 with `period_key_required` if it is omitted, rather than guessing a year. Use the `period` value from `GET /loans/{id}/documents`. Omit only for a single-period requirement. /public/v1/documents/{id}: patch: operationId: updateDocument tags: - Documents summary: Update document metadata description: Update document metadata. Currently only the document name can be updated. responses: "200": description: Document updated successfully content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicDocument" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true requestBody: required: true content: application/json: schema: type: object properties: name: type: string minLength: 1 maxLength: 255 required: - name delete: operationId: deleteDocument tags: - Documents summary: Delete a document description: Soft-delete a document by setting its deletion timestamp. The document will no longer appear in list results. If it satisfied a requirement it is also detached from it, and any requirement period left with no remaining file returns to `AWAITING_UPLOAD`. responses: "200": description: Document deleted successfully content: application/json: schema: type: object properties: message: type: string example: Document deleted "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true /public/v1/webhook-endpoints: post: operationId: createWebhookEndpoint tags: - Webhooks summary: Register a webhook endpoint description: Register an HTTPS endpoint to receive event deliveries. The signing secret is returned ONCE in this response and never again — store it securely. Private/loopback URLs are rejected. parameters: - in: header name: Idempotency-Key required: false schema: type: string maxLength: 255 description: Optional opaque key (e.g. a UUID) to make this write safely retryable. A replay with the same key returns the original response instead of repeating the side effect. responses: "201": description: Endpoint created (includes the one-time signing secret) content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicWebhookEndpointWithSecret" "400": description: Invalid URL (non-https / private host) or input "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 requestBody: required: true content: application/json: schema: type: object properties: name: type: string minLength: 1 maxLength: 100 url: type: string format: starts_with pattern: ^https:\/\/.* description: HTTPS URL; private/loopback/metadata hosts are rejected. subscribedEvents: minItems: 1 type: array items: type: string enum: - loan.created - loan.status_changed - loan.funded - document.uploaded - document.signed required: - name - url - subscribedEvents get: operationId: listWebhookEndpoints tags: - Webhooks summary: List webhook endpoints description: List your organization's webhook endpoints. The signing secret is never returned. responses: "200": description: Webhook endpoints content: application/json: schema: type: object properties: data: type: array items: $ref: "#/components/schemas/PublicWebhookEndpoint" "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 /public/v1/webhook-endpoints/{id}: get: operationId: getWebhookEndpoint tags: - Webhooks summary: Get a webhook endpoint description: Retrieve a single webhook endpoint by ID, including its target URL, subscribed event types, and status. responses: "200": description: Webhook endpoint content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicWebhookEndpoint" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true patch: operationId: updateWebhookEndpoint tags: - Webhooks summary: Update a webhook endpoint description: Update a webhook endpoint — change its target URL, subscribed event types, or status. Only the fields provided in the request body are changed. parameters: - in: header name: Idempotency-Key required: false schema: type: string maxLength: 255 description: Optional opaque key (e.g. a UUID) to make this write safely retryable. A replay with the same key returns the original response instead of repeating the side effect. - in: path name: id schema: type: string required: true responses: "200": description: Webhook endpoint updated content: application/json: schema: type: object properties: data: $ref: "#/components/schemas/PublicWebhookEndpoint" "400": description: Invalid URL or input "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 requestBody: required: true content: application/json: schema: type: object properties: name: type: string minLength: 1 maxLength: 100 url: type: string format: starts_with pattern: ^https:\/\/.* description: HTTPS URL; private/loopback/metadata hosts are rejected. subscribedEvents: minItems: 1 type: array items: type: string enum: - loan.created - loan.status_changed - loan.funded - document.uploaded - document.signed status: type: string enum: - active - paused - disabled delete: operationId: deleteWebhookEndpoint tags: - Webhooks summary: Delete a webhook endpoint description: Soft-delete (disable) a webhook endpoint. Delivery history is retained. parameters: - in: header name: Idempotency-Key required: false schema: type: string maxLength: 255 description: Optional opaque key (e.g. a UUID) to make this write safely retryable. A replay with the same key returns the original response instead of repeating the side effect. - in: path name: id schema: type: string required: true responses: "200": description: Endpoint deleted content: application/json: schema: type: object properties: message: type: string example: Webhook endpoint deleted "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 /public/v1/webhook-endpoints/{id}/rotate-secret: post: operationId: rotateWebhookSecret tags: - Webhooks summary: Rotate a webhook signing secret description: Generate a new signing secret. It is returned ONCE in this response and never again. parameters: - in: header name: Idempotency-Key required: false schema: type: string maxLength: 255 description: Optional opaque key (e.g. a UUID) to make this write safely retryable. A replay with the same key returns the original response instead of repeating the side effect. - in: path name: id schema: type: string required: true responses: "200": description: New signing secret (shown once) content: application/json: schema: type: object properties: data: type: object properties: secret: type: string "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 /public/v1/webhook-endpoints/{id}/test: post: operationId: testWebhookEndpoint tags: - Webhooks summary: Send a test event description: Synchronously deliver a `test.ping` event to the endpoint and return the result. Does not count toward auto-disable, and is intentionally NOT idempotency-keyed (safe to repeat). responses: "200": description: Test delivery result content: application/json: schema: type: object properties: data: type: object properties: success: type: boolean statusCode: type: integer error: type: string responseTimeMs: type: integer eventId: type: string "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true /public/v1/webhook-endpoints/{id}/deliveries: get: operationId: listWebhookDeliveries tags: - Webhooks summary: List delivery attempts for an endpoint description: Retrieve recent delivery attempts for a webhook endpoint, including status, response codes, and timestamps, to help debug your integration. responses: "200": description: Delivery attempts (most recent first) content: application/json: schema: type: object properties: data: type: array items: $ref: "#/components/schemas/PublicWebhookDelivery" meta: $ref: "#/components/schemas/PaginationMeta" "400": description: Validation Error content: application/json: schema: type: object properties: success: type: boolean enum: - false error: type: array items: {} data: {} required: - success - error - data "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "404": description: Requested resource not found content: application/json: schema: type: object properties: code: type: string example: loan.not_found message: type: string example: Loan not found "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 parameters: - in: path name: id schema: type: string required: true - in: query name: limit schema: default: 50 type: number minimum: 1 maximum: 100 - in: query name: offset schema: type: number minimum: 0 - in: query name: cursor schema: type: string - in: query name: status schema: type: string enum: - pending - delivered - failed - retrying /public/v1/webhook-events: get: operationId: listWebhookEventTypes tags: - Webhooks summary: List supported webhook event types description: List the webhook event types you can subscribe an endpoint to, with a short description of each. responses: "200": description: Supported event types content: application/json: schema: type: object properties: data: type: array items: type: object properties: type: type: string description: type: string "401": description: Authentication failed - Invalid or expired API key content: application/json: schema: type: object properties: code: type: string example: auth.invalid_key message: type: string example: Invalid or expired API key "403": description: Insufficient permissions for this operation content: application/json: schema: type: object properties: code: type: string example: auth.insufficient_permissions message: type: string example: Missing required permission "429": description: Rate limit exceeded - Too many requests content: application/json: schema: type: object properties: code: type: string example: rate_limit.exceeded message: type: string example: Too many requests retryAfter: type: integer example: 45 components: securitySchemes: ApiKeyAuth: type: http scheme: bearer bearerFormat: API Key description: "API key obtained from Pioneer dashboard. Format: pk_live_xxx or pk_test_xxx" schemas: PublicLoan: type: object properties: id: type: string loanNumber: type: - string - "null" status: type: - string - "null" type: anyOf: - type: string enum: - business - personal - type: "null" centsAmount: anyOf: - type: integer minimum: -9007199254740991 maximum: 9007199254740991 - type: "null" centsMonthlyPayment: anyOf: - type: integer minimum: -9007199254740991 maximum: 9007199254740991 - type: "null" monthsTerm: anyOf: - type: integer minimum: -9007199254740991 maximum: 9007199254740991 - type: "null" monthsInterestOnly: anyOf: - type: integer minimum: -9007199254740991 maximum: 9007199254740991 - type: "null" paymentFrequency: anyOf: - type: string enum: - weekly - biweekly - monthly - quarterly - semi-annual - annual - lump sum - irregular payments - type: "null" rateType: anyOf: - type: string enum: - fixed - variable - type: "null" bpsRate: anyOf: - type: integer minimum: -9007199254740991 maximum: 9007199254740991 - type: "null" purpose: type: - string - "null" purposeNarrative: type: - string - "null" applicationDate: type: - string - "null" closeDate: type: - string - "null" maturityDate: type: - string - "null" firstPaymentDate: type: - string - "null" product: anyOf: - type: object properties: id: type: string name: type: string required: - id - name additionalProperties: false - type: "null" stage: anyOf: - type: object properties: id: type: string name: type: string required: - id - name additionalProperties: false - type: "null" createdAt: type: string updatedAt: type: string required: - id - loanNumber - status - type - centsAmount - centsMonthlyPayment - monthsTerm - monthsInterestOnly - paymentFrequency - rateType - bpsRate - purpose - purposeNarrative - applicationDate - closeDate - maturityDate - firstPaymentDate - product - stage - createdAt - updatedAt additionalProperties: false PaginationMeta: oneOf: - type: object properties: mode: type: string const: offset limit: type: number offset: type: number hasMore: type: boolean required: - mode - limit - offset - hasMore additionalProperties: false - type: object properties: mode: type: string const: cursor limit: type: number hasMore: type: boolean nextCursor: type: - string - "null" required: - mode - limit - hasMore - nextCursor additionalProperties: false PublicProduct: type: object properties: id: type: string name: type: string description: type: - string - "null" monthsTerm: type: number monthsAmortization: type: number rateType: type: string enum: - fixed - variable paymentFrequency: type: string enum: - weekly - biweekly - monthly - quarterly - semi-annual - annual - lump sum - irregular payments required: - id - name - description - monthsTerm - monthsAmortization - rateType - paymentFrequency additionalProperties: false PublicBusiness: type: object properties: id: type: string name: type: string dba: type: - string - "null" entityType: type: - string - "null" einLast4: type: - string - "null" state: type: - string - "null" naics: anyOf: - type: string maxLength: 6 - type: "null" role: type: - string - "null" required: - id - name - dba - entityType - einLast4 - state - naics - role additionalProperties: false PublicBusinessDetail: type: object properties: id: type: string name: type: string dba: type: - string - "null" entityType: type: - string - "null" einLast4: type: - string - "null" state: type: - string - "null" naics: anyOf: - type: string maxLength: 6 - type: "null" role: type: - string - "null" ein: type: - string - "null" tinType: type: - string - "null" incorporationDate: type: - string - "null" phone: type: - string - "null" email: type: - string - "null" url: type: - string - "null" numberOfEmployees: type: - number - "null" locations: type: array items: type: object properties: id: type: string address: type: string address2: type: - string - "null" city: type: - string - "null" state: type: - string - "null" zipCode: type: - string - "null" required: - id - address - address2 - city - state - zipCode additionalProperties: false required: - id - name - dba - entityType - einLast4 - state - naics - role - ein - tinType - incorporationDate - phone - email - url - numberOfEmployees - locations additionalProperties: false PublicPerson: type: object properties: id: type: string firstName: type: string lastName: type: string ssnLast4: type: - string - "null" role: type: - string - "null" ownershipPercent: type: - number - "null" required: - id - firstName - lastName - ssnLast4 - role - ownershipPercent additionalProperties: false PublicPersonDetail: type: object properties: id: type: string firstName: type: string lastName: type: string ssnLast4: type: - string - "null" role: type: - string - "null" ownershipPercent: type: - number - "null" middleName: type: - string - "null" suffix: type: - string - "null" ssn: type: - string - "null" email: type: - string - "null" phone: type: - string - "null" dob: type: - string - "null" citizenship: type: - string - "null" maritalStatus: type: - string - "null" locations: type: array items: type: object properties: id: type: string address: type: string address2: type: - string - "null" city: type: - string - "null" state: type: - string - "null" zipCode: type: - string - "null" required: - id - address - address2 - city - state - zipCode additionalProperties: false required: - id - firstName - lastName - ssnLast4 - role - ownershipPercent - middleName - suffix - ssn - email - phone - dob - citizenship - maritalStatus - locations additionalProperties: false PublicDocument: type: object properties: id: type: string name: type: string type: type: string size: type: number category: type: - string - "null" uploadedAt: type: string requirement: anyOf: - type: object properties: id: type: string name: type: string status: type: - string - "null" module: type: - string - "null" entityType: type: string enum: - loan - person - business entityID: type: string period: type: - string - "null" required: - id - name - status - module - entityType - entityID - period additionalProperties: false - type: "null" required: - id - name - type - size - category - uploadedAt - requirement additionalProperties: false PublicTask: type: object properties: id: type: string loanId: type: string componentId: type: - string - "null" action: type: string triggerAction: type: - string - "null" status: type: string enum: - open - completed completedAt: type: - string - "null" required: - id - loanId - componentId - action - triggerAction - status - completedAt additionalProperties: false PublicDecision: type: object properties: productId: type: string productName: type: string verdict: type: string enum: - approved - declined reason: type: - string - "null" required: - productId - productName - verdict - reason additionalProperties: false PublicLoanStatus: type: object properties: status: type: - string - "null" stage: anyOf: - type: object properties: id: type: string name: type: string required: - id - name additionalProperties: false - type: "null" required: - status - stage additionalProperties: false PublicTimelineEvent: type: object properties: timestamp: type: - string - "null" status: type: - string - "null" changedBy: type: - string - "null" note: type: - string - "null" required: - timestamp - status - changedBy - note additionalProperties: false PublicLoanBoarding: type: object properties: loanId: type: string status: type: - string - "null" releasable: type: boolean released: type: boolean releasedAt: type: - string - "null" releasedBy: type: - string - "null" boarded: type: boolean boardedAt: type: - string - "null" coreRecordNumber: type: - string - "null" required: - loanId - status - releasable - released - releasedAt - releasedBy - boarded - boardedAt - coreRecordNumber additionalProperties: false PublicMessage: type: object properties: id: type: string content: type: string authorId: type: - string - "null" authorName: type: - string - "null" parentMessageId: type: - string - "null" createdAt: type: - string - "null" required: - id - content - authorId - authorName - parentMessageId - createdAt additionalProperties: false PublicDisbursement: type: object properties: id: type: string loanId: type: string category: type: - string - "null" centsAmount: anyOf: - type: integer minimum: -9007199254740991 maximum: 9007199254740991 - type: "null" recipientName: type: - string - "null" recipientType: type: - string - "null" paymentMethod: type: - string - "null" status: type: - string - "null" notes: type: - string - "null" createdAt: type: - string - "null" required: - id - loanId - category - centsAmount - recipientName - recipientType - paymentMethod - status - notes - createdAt additionalProperties: false PublicGLAccount: type: object properties: id: type: string accountNumber: type: string name: type: string mainCategory: type: string category: type: string subcategory: type: - string - "null" accountType: type: string isActive: type: boolean required: - id - accountNumber - name - mainCategory - category - subcategory - accountType - isActive additionalProperties: false PublicCollateral: type: object properties: id: type: string loanId: type: string description: type: - string - "null" category: type: - string - "null" centsMarketValue: anyOf: - type: integer minimum: -9007199254740991 maximum: 9007199254740991 - type: "null" bpsAdvanceRate: anyOf: - type: integer minimum: -9007199254740991 maximum: 9007199254740991 - type: "null" centsPriorLienAmount: anyOf: - type: integer minimum: -9007199254740991 maximum: 9007199254740991 - type: "null" centsManualPriorLien: anyOf: - type: integer minimum: -9007199254740991 maximum: 9007199254740991 - type: "null" ownerType: type: - string - "null" ownerId: type: - string - "null" required: - id - loanId - description - category - centsMarketValue - bpsAdvanceRate - centsPriorLienAmount - centsManualPriorLien - ownerType - ownerId additionalProperties: false PublicDebt: type: object properties: id: type: string loanId: type: string creditorName: type: string debtType: type: - string - "null" centsOutstandingBalance: anyOf: - type: integer minimum: -9007199254740991 maximum: 9007199254740991 - type: "null" centsPaymentAmount: anyOf: - type: integer minimum: -9007199254740991 maximum: 9007199254740991 - type: "null" paymentFrequency: type: - string - "null" bpsInterestRate: anyOf: - type: integer minimum: -9007199254740991 maximum: 9007199254740991 - type: "null" isRefinanced: type: boolean isSecuredByCollateral: type: boolean required: - id - loanId - creditorName - debtType - centsOutstandingBalance - centsPaymentAmount - paymentFrequency - bpsInterestRate - isRefinanced - isSecuredByCollateral additionalProperties: false PublicWebhookEndpoint: type: object properties: id: type: string name: type: string url: type: string environment: type: string status: type: string subscribedEvents: type: array items: type: string consecutiveFailures: type: number lastSuccessAt: type: - string - "null" lastFailureAt: type: - string - "null" lastFailureReason: type: - string - "null" autoDisabledAt: type: - string - "null" createdAt: type: string required: - id - name - url - environment - status - subscribedEvents - consecutiveFailures - lastSuccessAt - lastFailureAt - lastFailureReason - autoDisabledAt - createdAt additionalProperties: false PublicWebhookEndpointWithSecret: type: object properties: id: type: string name: type: string url: type: string environment: type: string status: type: string subscribedEvents: type: array items: type: string consecutiveFailures: type: number lastSuccessAt: type: - string - "null" lastFailureAt: type: - string - "null" lastFailureReason: type: - string - "null" autoDisabledAt: type: - string - "null" createdAt: type: string secret: type: string required: - id - name - url - environment - status - subscribedEvents - consecutiveFailures - lastSuccessAt - lastFailureAt - lastFailureReason - autoDisabledAt - createdAt - secret additionalProperties: false PublicWebhookDelivery: type: object properties: id: type: string event: type: string eventId: type: string status: type: string attempts: type: number maxAttempts: type: number responseStatus: type: - number - "null" responseTimeMs: type: - number - "null" nextRetryAt: type: - string - "null" deliveredAt: type: - string - "null" failedAt: type: - string - "null" createdAt: type: string required: - id - event - eventId - status - attempts - maxAttempts - responseStatus - responseTimeMs - nextRetryAt - deliveredAt - failedAt - createdAt additionalProperties: false