> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.spreadspace.app/get-started/quick-start/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.spreadspace.app/_mcp/server. # Quick start 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. 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` and `extractions:read`. The default preset covers this. Put the key in your environment: ```bash export SPREADSPACE_API_KEY="ss_test_..." ``` ## 2. Install **`TypeScript`** ```bash title="TypeScript" npm i @spreadspace/sdk ``` **`Python`** ```bash title="Python" pip install spreadspace ``` **`C#`** ```bash title="C#" dotnet add package SpreadSpace ``` Then download the sample file the run uploads: ```bash 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. **`TypeScript`** ```ts title="TypeScript" import { SpreadSpace } from '@spreadspace/sdk'; const client = new SpreadSpace({ apiKey: process.env.SPREADSPACE_API_KEY! }); ``` **`Python`** ```python title="Python" import os from spreadspace import SpreadSpace client = SpreadSpace(api_key=os.environ["SPREADSPACE_API_KEY"]) ``` **`C#`** ```csharp title="C#" using SpreadSpace; using var client = new SpreadSpaceClient(Environment.GetEnvironmentVariable("SPREADSPACE_API_KEY")); ``` **`cURL`** ```bash title="cURL" API="https://api.spreadspace.app" AUTH="Authorization: Bearer $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](/get-started/embed-walkthrough) asks for that loan. **`TypeScript`** ```ts title="TypeScript" 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); ``` **`Python`** ```python title="Python" borrower = next(iter(client.borrowers.list())) loan = next(iter(client.loans.list(borrower_id=borrower["borrower_id"]))) print(borrower["name"], loan["name"]) print("loan_id:", loan["loan_id"]) ``` **`C#`** ```csharp title="C#" var borrower = (await client.Borrowers.List().ToListAsync())[0]; var borrowerId = borrower.GetProperty("borrower_id").GetString()!; var loan = (await client.Loans.List(borrowerId: borrowerId).ToListAsync())[0]; var loanId = loan.GetProperty("loan_id").GetString()!; Console.WriteLine($"{borrower.GetProperty("name")} {loan.GetProperty("name")}"); Console.WriteLine($"loan_id: {loanId}"); ``` **`cURL`** ```bash title="cURL" BORROWER_ID=$(curl -s "$API/api/borrowers?limit=1" -H "$AUTH" | jq -r '.data[0].borrower_id') LOAN_ID=$(curl -s "$API/api/borrowers/$BORROWER_ID/loans?limit=1" -H "$AUTH" | jq -r '.data[0].loan_id') echo "loan_id: $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. **`TypeScript`** ```ts title="TypeScript" 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.extracted_document_ids as string[]; ``` **`Python`** ```python title="Python" job = client.documents.upload( "tax_form_1065.pdf", loan_id=loan["loan_id"], sample_document="tax-1065-2024", ) status = job.wait() # polls to COMPLETED or FAILED document_id = status["extracted_document_ids"][0] ``` **`C#`** ```csharp title="C#" var job = await client.Documents.UploadAsync( "tax_form_1065.pdf", loanId: loanId, sampleDocument: "tax-1065-2024"); var status = await job.WaitAsync(); // polls to COMPLETED or FAILED var documentId = status.GetProperty("extracted_document_ids")[0].GetString()!; ``` **`cURL`** ```bash title="cURL" PRESIGN=$(curl -s -X POST "$API/api/documents/presigned-url" \ -H "$AUTH" -H "Content-Type: application/json" \ -d "{\"file_name\":\"tax_form_1065.pdf\",\"content_type\":\"application/pdf\",\"loan_id\":\"$LOAN_ID\"}") JOB_ID=$(echo "$PRESIGN" | jq -r '.job_id') UPLOAD_URL=$(echo "$PRESIGN" | jq -r '.upload_url') # The Content-Type must equal the one the URL was signed for. curl -s -X PUT "$UPLOAD_URL" -H "Content-Type: application/pdf" --upload-file tax_form_1065.pdf curl -s -X POST "$API/api/documents/$JOB_ID/confirm-upload" \ -H "$AUTH" -H "Content-Type: application/json" \ -d '{"sample_document":"tax-1065-2024"}' # {"job_id":"...","status":"PROCESSING","sample":true} STATUS="PROCESSING" until [ "$STATUS" = "COMPLETED" ] || [ "$STATUS" = "FAILED" ]; do sleep 3 STATUS=$(curl -s "$API/api/documents/$JOB_ID/status" -H "$AUTH" | jq -r '.status') done DOCUMENT_ID=$(curl -s "$API/api/documents/$JOB_ID/status" -H "$AUTH" | jq -r '.extracted_document_ids[0]') ``` Read the Tax Return by document id and print the filer and one income line. The typed read returns the Tax Return object 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. **`TypeScript`** ```ts title="TypeScript" const taxReturn = await client.extractions.getTaxReturn(borrower.borrower_id, documentId); 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 ``` **`Python`** ```python title="Python" tax_return = client.extractions.get_tax_return(borrower["borrower_id"], document_id) if tax_return["form"] != "1065": raise SystemExit(f"expected a Form 1065, got {tax_return['form']}") print(tax_return["form"]) # 1065 print(tax_return["data"]["header"]["entity_name"]) # Greenfield Construction LLC print(tax_return["data"]["income"]["line_1a_gross_receipts"]) # 9200000.00 ``` **`C#`** ```csharp title="C#" var taxReturn = await client.Extractions.GetTaxReturnAsync(borrowerId, documentId); var form1065 = taxReturn.AsForm1065() ?? throw new InvalidOperationException($"expected a Form 1065, got {taxReturn.Form}"); Console.WriteLine(taxReturn.Form); // 1065 Console.WriteLine(form1065.Data.Header.EntityName); // Greenfield Construction LLC Console.WriteLine(form1065.Data.Income.Line1aGrossReceipts); // 9200000.00 ``` **`cURL`** ```bash title="cURL" curl -s "$API/api/borrowers/$BORROWER_ID/extractions/$DOCUMENT_ID/tax-return" -H "$AUTH" \ | jq '{form, filer: .data.header.entity_name, gross_receipts: .data.income.line_1a_gross_receipts}' # {"form":"1065","filer":"Greenfield Construction LLC","gross_receipts":"9200000.00"} ``` ## 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](/api/api-reference/tax-returns/object). Test mode returns premade extractions at no charge: an upload through an `ss_test_` key always answers with one of the [sample documents](/api/sample-documents), so you can exercise the whole loop before a single page is parsed. ## Where next #### [API Reference](/api) Every endpoint: borrowers, loans, document packages, extractions, webhooks, and embed sessions. #### [SDKs](/sdks/overview) Official TypeScript, Python, and C# clients: typed errors, pagination, uploads, and webhook verification. #### [Embed](/embed/overview) Render the workspace or document review panel inside your own app: React component in the browser, handle minting on your server. #### [API integration guide](/get-started/walkthrough) Receive finalized spreads and store the approved figures in your application. #### [Agent-assisted integration](/get-started/agent-assisted-integration) Hand the build to a coding agent: the skill and standing rules it follows. ### Resources * [OpenAPI spec](https://api.spreadspace.app/v1/openapi.json): the public contract as one JSON document. * Postman: the [collection](https://spreadspace.app/postman/spreadspace-api.postman_collection.json) and the [test-mode environment](https://spreadspace.app/postman/spreadspace-test-mode.postman_environment.json). * Status probe: `GET https://api.spreadspace.app/health` returns `200` with the body `Healthy`. * [Changelog](/api/changelog): what changed on the wire. * [Agent index](https://docs.spreadspace.app/llms.txt): every page of this site as Markdown, for a coding agent. > Upload a sample document and retrieve its structured data with the API.