Skip to navigation

Quick start

Upload a sample document and retrieve its structured data with the API.

Create a test key, upload a sample Form 1065, and retrieve the Tax Return response. Test mode uses sample extraction results at no charge. Use it to test your integration before processing your own documents in live mode.

1. Get a test key

In the dashboard, open Settings, then Sandbox API under API, and press Create sandbox key. In the Create API key dialog, leave the Backend preset selected and press Create key. The key is shown once, on the API key created screen. Copy it now.

Test mode needs no subscription: the Sandbox API page, test keys and webhook endpoints are open before your workspace subscribes. Live keys need an active subscription.

The key starts with ss_test_. It routes every call to the sandbox, an isolated workspace that is created and seeded the first time you mint a test key, with three borrowers and five loans. The calls below need borrowers:read, loans:read, documents:write, documents:read, extractions:read, spreads:write and spreads:read. The Backend preset covers this. A sandbox key created from the preset before 2026-10-29 lacks spreads:write; if step 4 answers 403 insufficient_scope, create a new sandbox key.

Put the key in your environment:

export SPREADSPACE_API_KEY="ss_test_..."

2. Install

Requires @spreadspace/sdk, spreadspace or SpreadSpace 2.2.0 or later. An earlier release cannot finalize the sample spread, and one below 2.1.0 cannot upload.

npm i @spreadspace/sdk@^3.0.0

Then download the sample file the run uploads:

curl -fsSLO https://spreadspace.app/sandbox-docs/tax_form_1065.pdf

3. Run it

Choose a language and follow the steps in order. The examples form one program and reuse the IDs from each response. The cURL examples require jq.

Construct the client from the environment variable. An ss_test_ key selects the sandbox; the base URL is the same in both modes.

import { SpreadSpace } from '@spreadspace/sdk';
const client = new SpreadSpace({ apiKey: process.env.SPREADSPACE_API_KEY! });

Select a sample borrower, then select one of that borrower’s loans. The examples use the first result from each list; any sample loan will work. Each example prints the loan’s loan_id. Keep it: the Embed integration guide asks for that loan.

const [borrower] = await client.borrowers.list<{ borrower_id: string; name: string }>().toArray();
const [loan] = await client.loans
.list<{ loan_id: string; name: string | null }>({ borrowerId: borrower.borrower_id })
.toArray();
console.log(borrower.name, loan.name);
console.log('loan_id:', loan.loan_id);

Upload the sample and wait for completion. The SDK handles the upload URL, file transfer, and confirmation. In test mode, sample_document selects the sample result; this example uses tax-1065-2024. The confirmation response includes "sample": true, and the job status includes the extracted document ID.

const job = await client.documents.upload('./tax_form_1065.pdf', {
loanId: loan.loan_id,
sampleDocument: 'tax-1065-2024',
});
const status = await job.wait(); // polls to completed or failed
const [documentId] = status.document_ids as string[];

Read the Tax Return by document id and print the filer and one income line. The typed read returns the document, its Tax Return object in report_data as its published schema states it: form names the return and data carries its lines, grouped the way the form prints them. A Form 1065 has a header and an income group; the check on form narrows the object to that shape.

const taxReturn = (await client.documents.getTaxReturn(documentId)).report_data;
if (taxReturn?.form !== '1065') throw new Error(`expected a Form 1065, got ${taxReturn?.form}`);
console.log(taxReturn.form); // 1065
console.log(taxReturn.data.header.entity_name); // Greenfield Construction LLC
console.log(taxReturn.data.income.line_1a_gross_receipts); // 9200000.00

4. Finalize the sample spread

The upload gave the loan a sample spread: the figures you just read, laid out the way an analyst’s spread holds them. In your own workspace an analyst finalizes a spread from the board menu; in the sandbox the finalize trigger stands in for that click. It runs the same operation, so the snapshot it creates and the spread.finalized event it sends are what a live finalize produces, with livemode: false. Pass your own user’s id as external_user_id and it comes back on the snapshot, exactly as an embed session’s finalize stamps it.

Finalize, then read the snapshot the response names and print the status, the user and one figure.

const finalized = await client.sandbox.finalizeSpread({ loanId: loan.loan_id, externalUserId: 'analyst-42' });
const snapshot = await client.loans.attributes.retrieve(loan.loan_id, finalized.snapshot_id);
console.log(snapshot.status); // final
console.log(snapshot.finalized_by_external_user_id); // analyst-42
console.log(snapshot.payload.attributes.tax_return_tax_gross_receipts.values['2025']); // 9200000.00
console.log('event_id:', finalized.event_id);

A second finalize answers 409 spread_already_finalized until the spread is reopened: POST /api/sandbox/loans/{loan_id}/reopen (client.sandbox.reopenSpread), which sends spread.reopened, after which the next finalize mints a new snapshot at a higher version. An endpoint registered with your test key and subscribed to spread.finalized receives the event, its id equal to event_id; the API integration guide builds that receiver.

What you got

You read a Tax Return object: form names the return, data carries the form’s lines grouped as printed (header, income, deductions, the schedules), k1_partners holds the attached Schedule K-1s, statements the supporting statements, and metadata the document context. Every field is on The Tax Return object. Test mode returns premade extractions at no charge: an upload through an ss_test_ key always answers with one of the sample documents, so you can exercise the whole loop before a single page is parsed.

You also finalized a spread the way an analyst does and read the snapshot that holds its figures: the same two objects your backend will receive from a live workspace. See Spreads for the snapshot, key by key, and Sample documents for what the sandbox holds.

Where next

Resources