Changelog

What changed on the public API surface and in the SDK packages, newest first.

This page lists changes to the public API surface and to the official SDK packages, newest first, one heading per day. API entries cover endpoints added or removed, request and response shapes, status codes, webhook events, scopes and rate-limit lanes, and are dated by the day the change reached the API. SDK entries are dated by the day the version reached its registry.

Every API entry below happened under the 2026-07-19 version stamp; a new stamp would appear here as its own entry. How a request pins a stamp, and which stamps are accepted, is on Versioning.

2026-09-05

  • @spreadspace/sdk 0.3.0 (npm), spreadspace 0.3.0 (PyPI) and SpreadSpace 0.3.0 (NuGet) published. Notable changes since 0.1.8 (0.2.0 was never published on its own; its changes ship here):
    • Exports replace async operations. The async operations resource is gone from all three packages. client.exports (Exports in C#) creates, reads, lists and cancels an export and waits on it, and a downloadLink / download_link / GetDownloadLinkAsync read returns the bundle link with its expiry once the export has succeeded (an export_not_ready error before that). The create call’s deliveryMode / delivery_mode parameter is gone.
    • Create requires borrower_id, loan_id and document_ids, and the upload helpers require the loan id, so a request the server would answer with 400 no longer compiles or type-checks.
    • export.succeeded, export.failed, export.cancelled, memo_render.succeeded, memo_render.failed and memo_render.cancelled are in the typed event unions, with matching receiver handlers.
    • Typed attribute reads in Python (AttributesEnvelope, AttributeVersions, AttributeSnapshot) and C# (GetAsync, VersionsAsync and GetSnapshotAsync return typed records, which changes their return types; the untyped reads remain as GetRawAsync, VersionsRawAsync and GetSnapshotRawAsync).
    • versions(loanId, status) filters the snapshot history to one status, and deliveries(endpointId, ...) takes status, event_type and loan_id.
  • @spreadspace/react 0.1.2 and @spreadspace/embed 0.1.3 published (npm).

2026-09-04

  • Six delete operations answer 204 instead of 200: DELETE /api/borrowers/{id}, DELETE /api/loans/{id}, DELETE /api/jobs/{jobId}, DELETE /api/borrowers/{borrowerId}/extractions/{docId}, DELETE /api/embed/sessions/{sessionId} and DELETE /api/webhooks/{id}.
  • Three create operations answer 201 instead of 200: POST /api/borrowers, POST /api/borrowers/{borrowerId}/loans and POST /api/webhooks.
  • 402 is declared on every operation except GET /api/me and POST /api/sandbox/reset. On POST /api/documents/presigned-url, POST /api/documents/presigned-urls-batch, POST /api/documents/{jobId}/confirm-upload, POST /api/documents/confirm-uploads and POST /api/intake, the 402 body’s error is one of subscription_required, spend_cap_reached or insufficient_credits, and a 429 can carry the flat usage_throttled body with a Retry-After header.
  • Fields that became required: loan_id on POST /api/documents/presigned-url; borrower_id, loan_id and document_ids on POST /api/extractions/exports; the loanId query parameter on GET and PUT /api/borrowers/{borrowerId}/extractions/fin-ebitda-addbacks; the format query parameter (csv or json) on GET /api/borrowers/{borrowerId}/extractions/{docId}/export, which also answers 413 doc_too_large for a document over 10,000 line items.
  • POST /api/borrowers/{borrowerId}/loans: borrower_id left the request body; the path names the borrower.
  • entity_key on the rows of GET and PUT /api/borrowers/{borrowerId}/extractions/fin-ebitda-addbacks is optional and nullable; it was required.
  • Response bodies are now declared for GET /api/borrowers, GET /api/loans, GET /api/borrowers/{borrowerId}/loans and GET /api/webhooks (data, limit, next_cursor), GET /api/borrowers/{id}/jobs, GET /api/borrowers/{id}/jobs/{jobId}/results, GET /api/borrowers/{borrowerId}/extractions/query, POST /api/borrowers/{borrowerId}/extractions/query-bulk and POST /api/sandbox/reset (reset, sandbox_tenant_present, reseeded, external_purge_errors). They were untyped on the reference before.
  • POST /api/sandbox/reset accepts Idempotency-Key and declares 409. POST /api/webhooks/{id}/rotate declares 409 again (rotating a revoked endpoint).
  • Every operation’s reference page now states the scope it requires and its rate-limit lane: api (200 requests per minute), upload (1000), export (120) or strict (10, for POST /api/sandbox/reset and POST /api/webhooks/{id}/deliveries/{deliveryId}/replay).
  • The keying query parameter left GET /api/borrowers/{borrowerId}/extractions; it had no effect for an API key.

2026-09-02

  • GET /api/extractions/exports/{exportId}/download added: answers 302 to a short-lived link for a succeeded export’s bundle, and 404 until the export has succeeded or once it has expired.
  • The export object took its final shape: result_url, result_expires_at and result are replaced by download_url and download_expires_at, minted fresh on every read while the export is succeeded and unexpired; document_counts (requested, exported, skipped), skipped_document_ids and bundle (name, size_bytes, line_item_count) are new; links gains download. A cancel that lands on a running export moves it to cancelling, then cancelled at the next document boundary.
  • Six webhook events: export.succeeded (the bundle is ready; fetch the export for a download link, the event never carries one), export.failed (reason is the export’s error code), export.cancelled, memo_render.succeeded (pull the PDF through the memo’s own download endpoint), memo_render.failed (reason is the render’s error code) and memo_render.cancelled (a queued render was cancelled before it started).
  • A document’s company attachment carries cross_kind (true when a Tax Return or Personal Financial Statement was filed by hand under an entity of the other kind), and extracted-document list items carry entity_unlinked (boolean).

2026-09-01

  • Async operations were folded into the resources they served. Removed: POST /api/async-operations/extraction_export, GET /api/async-operations, GET /api/async-operations/{operationId} and POST /api/async-operations/{operationId}/cancel. Their replacements, in the Extractions group: POST /api/extractions/exports (answers 202; a retry with the same Idempotency-Key replays that response for 24 hours, after which the same key returns the existing export with 200), GET /api/extractions/exports (its items key is exports), GET /api/extractions/exports/{exportId} and POST /api/extractions/exports/{exportId}/cancel (409 once the export has finished; an already-cancelled export is returned unchanged). The export object carries export_id, borrower_id, loan_id, format, status, progress, links and its timestamps; there is no kind and no operation_id.
  • Credit memo renders follow the same shape: GET /api/loans/{loanId}/memos/{memoId}/renders/{renderId} and POST /api/loans/{loanId}/memos/{memoId}/renders/{renderId}/cancel added, and POST /api/loans/{loanId}/memos/{memoId}/renders answers 202 with the render (render_id, memo_id, loan_id, status, progress, links) instead of a generic operation.
  • Extracted-document list items carry company_attachments, a list in the shape of company_attachment.

2026-08-23

  • Removed GET /api/borrowers/{borrowerId}/extractions/financial-packets and GET /api/borrowers/{borrowerId}/extractions/{docId}/financial-packet. A packet’s statements now arrive as separate Balance Sheet, Profit Loss Statement and Cash Flow Statement documents, read through their own list and retrieve operations, each carrying cpa_engagement.
  • PUT /api/borrowers/{borrowerId}/extractions/fin-ebitda-addbacks takes an entityKey query parameter naming the company scope (business: or person: followed by the folded name; omit it for the whole-loan scope), and add-back rows carry entity_key.

2026-08-22

  • Added GET and PUT /api/borrowers/{borrowerId}/extractions/fin-ebitda-addbacks: list and replace the saved EBITDA add-back selections for a loan (loanId query parameter).

2026-08-18

  • GET /api/loans/{loanId}/attributes/versions takes status (final, machine or draft; any other value is 400).
  • GET /api/webhooks/{id}/deliveries takes loan_id, and each delivery row carries loan_id, the loan its event was about.
  • xlsx exports are limited to Bank Statements: an export that names any other document type with xlsx is rejected with 400 (then POST /api/async-operations/extraction_export, now POST /api/extractions/exports).
  • POST /api/sandbox/reset returned to the public reference: a test-mode key restores its seeded workspace (sandbox:reset; a live key gets 403 mode_mismatch). GET /api/organizations/{id} left the reference; GET /api/organizations and GET /api/me remain.
  • @spreadspace/sdk, spreadspace and SpreadSpace 0.1.8 published (0.1.7 was never published on its own; its change ships here): loans.attributes reads (the latest snapshot of a status, the version history, and one snapshot by id), a webhook receiver that verifies the signature, drops redeliveries it has already processed, dispatches per event and reads the snapshot a spread.finalized names, extraction.updated, spread.finalized and spread.reopened in the typed event unions, verifiers that accept several secrets for the rotation window, and external_user_id on the embed mint calls. In C#, ExtractionReadyPayload and DocumentProcessedPayload gained positional parameters, so code that constructs them positionally must re-check its arguments.

2026-08-17

  • Added GET /api/loans/{loanId}/attributes (the latest snapshot of a status), GET /api/loans/{loanId}/attributes/versions (the snapshot history) and GET /api/loans/{loanId}/attributes/{snapshotId} (one snapshot with its payload and manifest), under spreads:read. Snapshot reads carry finalized_by and finalized_by_external_user_id.
  • The webhook event catalog is part of the contract: document.processed, document.failed, extraction.ready, extraction.updated, job.completed, loan.classified and spread.finalized each declare their envelope and payload; subscribed_events is typed to that list plus *; the delivery headers SpreadSpace-Signature, SpreadSpace-Event-Id and User-Agent (SpreadSpace-Webhooks/1) are documented; GET /api/webhooks/{id}/deliveries declares its page shape.
  • New event spread.reopened: fires once per reopen of a finalized spread, naming the final snapshot that stays in force until the next spread.finalized.
  • Extracted-document list items carry extraction_version.

2026-08-15

  • Extracted-document list items carry company_attachment: which of the borrower’s companies a financial document belongs to (entity_name, entity_ein_last4, printed_name), as resolved for that read, or null.

2026-08-14

  • POST /api/embed/sessions answers with persistence: user when the mint carried an external_user_id (saved work comes back on that user’s next session), session when it did not.
  • POST /api/embed/sessions and POST /api/embed/iframe-urls accept display_name (requires external_user_id; at most 120 characters; echoed in the response).

2026-08-13

  • Typed retrieve and list pairs added under /api/borrowers/{borrowerId}/extractions: balance-sheets, profit-loss-statements, cash-flow-statements, debt-schedules, ar-agings and ap-agings (lists), and {docId}/bank-statement, {docId}/ar-aging and {docId}/ap-aging (single documents).
  • Removed from the public surface, replaced by those pairs, all GET under /api/borrowers/{borrowerId}/extractions: pl, balance-sheet, accounts-receivable, accounts-payable, financial-statements, ar-aging-summaries and {docId}/ar-aging-summary. GET /api/borrowers/{borrowerId}/extractions/reports/{reportType} remains the read by category slug.

2026-08-12

  • Added credit memo operations: GET /api/loans/{loanId}/memos (the memos saved on a loan, with their render state), POST /api/loans/{loanId}/memos/{memoId}/renders (202; requests a PDF render) and GET /api/loans/{loanId}/memos/{memoId}/pdf (a short-lived link to the latest rendered PDF; 404 until a render has finished).
  • Removed from the public reference and the SDKs, as workspace and session controls rather than server-to-server surface: the audit, compliance and usage reads under /api/borrowers/{id}, /api/loans/{id} and /api/organizations/{id}; the analyst review controls under /api/borrowers/{borrowerId}/extractions; GET /api/loans/{loanId}/spreads (its successor is the attribute reads added 2026-08-17); PATCH /api/jobs/{jobId}/move; POST /api/embed/exchange and POST /api/embed/bootstrap-events.

2026-08-08

  • Added GET /api/borrowers/{borrowerId}/extractions/personal-financial-statements (list all Personal Financial Statements).
  • POST /api/documents/{jobId}/confirm-upload responses carry duplicate_of_job_id (the prior job this upload duplicates; present only on a DUPLICATE status) and message.
  • @spreadspace/sdk, spreadspace and SpreadSpace 0.1.6 published (0.1.5 was never published on its own; its change ships here): the webhooks resource calls /api/webhooks/*, and internal model types that had reached the generated packages are removed. Supersedes @spreadspace/sdk 0.1.4 and spreadspace / SpreadSpace 0.1.2.

2026-08-07

  • Idempotency-Key left POST /api/webhooks and POST /api/webhooks/{id}/rotate, along with the 409 they declared.
  • Extracted-document list items carry entity_ein_last4.
  • The /api/ui-state operations (on the reference since 2026-07-31) left the public reference: a session and embed surface that an API key cannot call.

2026-08-04

  • The report template, report asset and dashboard operations (/api/report-templates, /api/report-assets, /api/me/tax-dashboards, /api/me/financial-dashboards and /api/me/bank-dashboards) left the public reference: session-only surface that an API key cannot call.

2026-08-03

  • Added GET /api/borrowers/{borrowerId}/extractions/tax-returns (list all Tax Returns). GET /api/borrowers/{borrowerId}/extractions/financial-statements and GET /api/borrowers/{borrowerId}/extractions/ar-aging-summaries were added the same day and replaced on 2026-08-13.

2026-08-02

  • Extracted-document list items carry assurance_level, and borrower owner records (GET /api/borrowers/{borrowerId}/extractions/owners) carry aliases, the name variants seen.

2026-08-01

  • Loans carry guarantors, a list of guarantor names: on POST /api/borrowers/{borrowerId}/loans, on PATCH /api/loans/{id} (omit the field to leave the list untouched, send a list to replace it, send an empty list to clear it) and in loan responses (null when none were declared or the caller may not read the loan file).

2026-07-31

  • POST /api/embed/sessions and POST /api/embed/iframe-urls accept external_user_id, an opaque, stable end-user id of at most 256 characters; the session response echoes it.
  • Added GET /api/borrowers/{borrowerId}/extractions/{docId}/financial-packet (removed 2026-08-23).

2026-07-30

  • @spreadspace/sdk, spreadspace and SpreadSpace 0.1.2 published: release wiring only, no API surface change. @spreadspace/sdk 0.1.3 (npm only): the API key is held in a private field so it cannot be read off the client by enumeration, serialization or a stray log. @spreadspace/sdk 0.1.4 (npm only): republished with no source change.
  • @spreadspace/react 0.1.0 and 0.1.1, and @spreadspace/embed 0.1.0, 0.1.1 and 0.1.2 published (npm), the first registry releases of the embed packages.

2026-07-23

  • Added GET /api/borrowers/{borrowerId}/extractions/{docId}/profit-loss-statement (retrieve a Profit Loss Statement).
  • Borrower owner records carry k1_form.

2026-07-19

  • 2026-07-19 is the latest API version. 2026-05-03 is still accepted as a header value and resolves to the same behavior.
  • Added GET /api/borrowers/{borrowerId}/extractions/{docId}/report-data: one document’s extraction payload whatever its category (the typed retrieve operations answer 404 outside their own type).
  • POST /api/sandbox/reset and GET /api/borrowers/{borrowerId}/extractions/analysis-context left the public reference (the reset returned on 2026-08-18).