> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.spreadspace.app/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.