Extracts personal information and tax details from IRS Form 1040 returns.
Form 1040 is the primary annual tax filing document submitted by U.S. individuals to report income, claim dependents, declare filing status, and calculate federal income tax liability. This template takes in Form 1040 U.S. Individual Income Tax Return and outputs markdown (.md) with the form's full text and layout structure, and JSON (.json) containing extracted taxpayer identification, dependent information, filing status, address, and tax-specific declarations per the extraction schema by using Extend's Parse, Extract primitives.
Converts the document into clean, layout-aware markdown plus structured blocks with spatial metadata.
blockOptions.text.agentic.enabledtruechangedchunkingStrategy.type"document"engine"parse_performance"You can learn more about Parse configuration in Extend's Parse documentation.
Pulls a defined set of fields from the document and returns them as structured JSON matching a schema.
schemacustom schema — 11 fieldschangedadvancedOptions.advancedMultimodalEnabledtruechangedadvancedOptions.reviewAgent.enabledtruechangedbaseProcessor"extraction_performance"You can learn more about Extract configuration in Extend's Extract documentation.
{
"name": "U.S. Individual Income Tax Return Processing Pipeline",
"steps": [
{
"name": "startTrigger1",
"type": "TRIGGER",
"next": [
{
"step": "parse1"
}
]
},
{
"name": "parse1",
"type": "PARSE",
"config": {
"parseConfig": {
"blockOptions": {
"text": {
"agentic": {
"enabled": true
}
}
},
"chunkingStrategy": {
"type": "document"
}
}
},
"next": [
{
"step": "extraction2"
}
]
},
{
"name": "extraction2",
"type": "EXTRACT",
"config": {
"extractorConfig": {
"schema": {
"type": "object",
"properties": {
"tax_year": {
"type": [
"string",
"null"
],
"description": "The tax year of the return (e.g., 2021)"
},
"dependents": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ssn": {
"type": [
"string",
"null"
],
"description": "Social security number of dependent"
},
"name": {
"type": [
"string",
"null"
],
"description": "First and last name of dependent"
},
"relationship": {
"type": [
"string",
"null"
],
"description": "Relationship to taxpayer (e.g., Daughter, Son, Parent)"
},
"qualifies_for_child_tax_credit": {
"type": [
"boolean",
"null"
],
"description": "Whether dependent qualifies for child tax credit"
}
}
},
"description": "List of dependents claimed on return"
},
"spouse_ssn": {
"type": [
"string",
"null"
],
"description": "Social security number of spouse (if joint return)"
},
"spouse_name": {
"type": [
"string",
"null"
],
"description": "First name, middle initial, and last name of spouse (if joint return)"
},
"home_address": {
"type": [
"string",
"null"
],
"description": "Full home address including street, city, state, and ZIP code"
},
"filing_status": {
"type": [
"string",
"null"
],
"description": "Filing status selected: Single, Married filing jointly, Married filing separately, Head of household, or Qualifying widow(er)"
},
"age_blindness_status": {
"type": [
"string",
"null"
],
"description": "Age/blindness status for standard deduction calculation (born before Jan 2, 1957 or blind)"
},
"primary_taxpayer_ssn": {
"type": [
"string",
"null"
],
"description": "Social security number of primary taxpayer"
},
"primary_taxpayer_name": {
"type": [
"string",
"null"
],
"description": "First name, middle initial, and last name of primary taxpayer"
},
"virtual_currency_transaction": {
"type": [
"string",
"null"
],
"description": "Whether taxpayer received, sold, exchanged, or disposed of virtual currency during tax year (Yes/No)"
},
"presidential_election_campaign_contribution": {
"type": [
"boolean",
"null"
],
"description": "Whether taxpayer or spouse elected to contribute $3 to Presidential Election Campaign fund"
}
}
},
"baseProcessor": "extraction_performance",
"advancedOptions": {
"reviewAgent": {
"enabled": true
},
"advancedMultimodalEnabled": true
}
}
}
}
]
}# U.S. Individual Income Tax Return Processing — Extend AI Skill
## What this pipeline does
This pipeline extracts structured tax filing information from Form 1040 (U.S. Individual Income Tax Return) by first parsing the document to markdown, then extracting key fields: taxpayer identity, filing status, dependent details, and tax-specific flags. The output is a clean JSON object ready for downstream compliance, filing, or audit workflows.
## When to use this
- **Tax preparation software** ingesting scanned or digital 1040s to prefill client profiles
- **Accounting firms** processing bulk tax returns for data entry validation and QA
- **Tax compliance systems** extracting dependent and filing status to trigger eligibility checks
- **IRS e-filing workflows** converting paper returns to structured XML-ready JSON
- **Taxpayer onboarding** capturing identity and address on first filing without manual keying
## Processor pipeline
### Step 1: Parse (`parse_performance` with agentic OCR)
**Purpose:** Convert Form 1040 (digital or scanned) into machine-readable markdown.
**Config chosen:**
- `blockOptions.text.agentic.enabled: true` — enables agentic OCR to handle checkboxes, form fields, and handwritten entries common in tax returns
- `chunkingStrategy: "document"` — keeps the entire form as one coherent block, preserving section relationships (dependents table, taxpayer info block, etc.)
**Why:** Tax forms have tightly structured layouts with interdependent sections (e.g., spouse name only appears if filing status is married). Agentic parsing understands form logic and context; document-level chunking prevents accidental splitting of dependent rows or address fields.
### Step 2: Extract (`extraction_performance` with review agent)
**Purpose:** Pull structured fields into JSON using a schema-driven extraction.
**Config chosen:**
- `baseProcessor: "extraction_performance"` — balances accuracy and speed for multi-page forms with dense tables (dependents)
- `advancedOptions.reviewAgent.enabled: true` — AI review agent catches extraction errors (e.g., swapped spouse/taxpayer names, dependent SSN misalignment)
- `advancedOptions.advancedMultimodalEnabled: true` — uses both text and image context to resolve ambiguous checkboxes or faded scans
**Why:** Tax forms are high-stakes; a single transposed SSN or wrong filing status breaks downstream systems. Review agent is worth the latency cost. Multimodal extraction handles the mix of printed and handwritten data on real-world 1040s.
---
## TypeScript implementation
---
## CLI equivalent
```bash
# Step 1: Parse Form 1040 to markdown
extend parse form_1040.pdf \
--engine parse_performance \
--agentic-ocr \
--chunk-strategy document
# Step 2: Extract structured fields
extend extract form_1040.pdf \
--schema form_1040_schema.json \
--base-processor extraction_performance \
--review-agent \
--advanced-multimodal
```
**form_1040_schema.json:**
```json
{
"type": "object",
"properties": {
"tax_year": {
"type": ["string", "null"],
"description": "The tax year of the return (e.g., 2021)"
},
"filing_status": {
"type": ["string", "null"],
"description": "Filing status selected: Single, Married filing jointly, Married filing separately, Head of household, or Qualifying widow(er)"
},
"primary_taxpayer_name": {
"type": ["string", "null"],
"description": "First name, middle initial, and last name of primary taxpayer"
},
"primary_taxpayer_ssn": {
"type": ["string", "null"],
"description": "Social security number of primary taxpayer"
},
"spouse_name": {
"type": ["string", "null"],
"description": "First name, middle initial, and last name of spouse (if joint return)"
},
"spouse_ssn": {
"type": ["string", "null"],
"description": "Social security number of spouse (if joint return)"
},
"home_address": {
"type": ["string", "null"],
"description": "Full home address including street, city, state, and ZIP code"
},
"virtual_currency_transaction": {
"type": ["string", "null"],
"description": "Whether taxpayer received, sold, exchanged, or disposed of virtual currency during tax year (Yes/No)"
},
"dependents": {
"type": "array",
"description": "List of dependents claimed on return",
"items": {
"type": "object",
"properties": {
"name": {
"type": ["string", "null"],
"description": "First and last name of dependent"
},
"ssn": {
"type": ["string", "null"],
"description": "Social security number of dependent"
},
"relationship": {
"type": ["string", "null"],
"description": "Relationship to taxpayer (e.g., Daughter, Son, Parent)"
},
"qualifies_for_child_tax_credit": {
"type": ["boolean", "null"],
"description": "Whether dependent qualifies for child tax credit"
}
}
}
},
"presidential_election_campaign_contribution": {
"type": ["boolean", "null"],
"description": "Whether taxpayer or spouse elected to contribute $3 to Presidential Election Campaign fund"
},
"age_blindness_status": {
"type": ["string", "null"],
"description": "Age/blindness status for standard deduction calculation (born before Jan 2, 1957 or blind)"
}
}
}
```
---
## Schema
### Root object
```json
{
"type": "object",
"properties": {
"tax_year": {
"type": ["string", "null"],
"description": "The tax year of the return (e.g., 2021, 2022). Extract this from the top-left corner of Form 1040. Required to match with tax filing deadline and rate tables."
},
"filing_status": {
"type": ["string", "null"],
"description": "Filing status selected: Single, Married filing jointly, Married filing separately, Head of household, or Qualifying widow(er). This is marked with a checkbox in the top section of the form. Determines standard deduction, tax brackets, and eligibility for certain credits."
},
"primary_taxpayer_name": {
"type": ["string", "null"],
"description": "First name, middle initial, and last name of the primary taxpayer exactly as shown on the form. Appears at the top of Form 1040, line 1(a). Must match SSN record for filing validation."
},
"primary_taxpayer_ssn": {
"type": ["string", "null"],
"description": "Social security number of primary taxpayer in the format XXX-XX-XXXX or with spaces. Located on line 1 of Form 1040. Critical for IRS matching and fraud detection."
},
"spouse_name": {
"type": ["string", "null"],
"description": "First name, middle initial, and last name of spouse (if joint return). Only populated for 'Married filing jointly' or 'Married filing separately' status. Appears on line 1(b) of Form 1040. Leave null if filing status is not married or if this is a single filer."
},
"spouse_ssn": {
"type": ["string", "null"],
"description": "Social security number of spouse (if joint return) in XXX-XX-XXXX format. Located on line 1 of Form 1040. Only applicable for married filing jointly/separately status. Leave null for single filers or if spouse SSN is not present."
},
"home_address": {
"type": ["string", "null"],
"description": "Full home address including street number and name, city, state abbreviation, and 5-digit ZIP code (e.g., '123 Main St, Springfield, IL 62701'). Appears in the top right section of Form 1040. Used for IRS correspondence and verification."
},
"virtual_currency_transaction": {
"type": ["string", "null"],
"description": "Whether taxpayer received, sold, exchanged, or disposed of virtual currency during tax year. Answer is 'Yes' or 'No' based on checkbox at the top of Form 1040. Critical for crypto tax compliance starting in 2021."
},
"dependents": {
"type": "array",
"description": "List of all dependents claimed on the return. Each dependent row in the Dependents section (usually lines 2-7 on Form 1040) corresponds to one object in this array. Empty array if no dependents.",
"items": {
"type": "object",
"properties": {
"name": {
"type": ["string", "null"],
"description": "First and last name of dependent exactly as shown in the Dependents table. Must match SSN for dependency claim validation."
},
"ssn": {
"type": ["string", "null"],
"description": "Social security number of dependent in XXX-XX-XXXX format. Located in the SSN column of the Dependents table. Used to link dependent to tax identification system."
},
"relationship": {
"type": ["string", "null"],
"description": "Relationship to taxpayer (e.g., Daughter, Son, Parent, Brother, Sister, Grandchild). Taken from the Relationship column of the Dependents table. Determines dependency test eligibility (e.g., child, relative, member of household)."
},
"qualifies_for_child_tax_credit": {
"type": ["boolean", "null"],
"description": "Whether dependent qualifies for child tax credit. Determined by a checkbox or indicator in the Dependents table (often in the rightmost columns). True if child is under 17 and meets relationship/residency tests; false otherwise. Leave null if status is unclear."
}
}
}
},
"presidential_election_campaign_contribution": {
"type": ["boolean", "null"],
"description": "Whether taxpayer or spouse elected to contribute $3 to Presidential Election Campaign fund. This is a checkbox near the signature section of Form 1040. True if checked, false if unchecked, null if not visible. Does not affect tax calculation but is reported to the FEC."
},
"age_blindness_status": {
"type": ["string", "null"],
"description": "Age/blindness status for standard deduction calculation. Extracted from checkboxes on Form 1040 line 3 or equivalent. Examples: 'Born before Jan 2import { ExtendClient } from "extend-ai";
import { z } from "zod";
import fs from "fs";
// Initialize the Extend client with API key from environment
const client = new ExtendClient({ token: process.env.EXTEND_API_KEY });
// Define the Zod schema for Form 1040 extraction
const form1040Schema = z.object({
tax_year: z.string().nullable().describe("The tax year of the return (e.g., 2021)"),
filing_status: z.string().nullable().describe("Filing status selected: Single, Married filing jointly, Married filing separately, Head of household, or Qualifying widow(er)"),
primary_taxpayer_name: z.string().nullable().describe("First name, middle initial, and last name of primary taxpayer"),
primary_taxpayer_ssn: z.string().nullable().describe("Social security number of primary taxpayer"),
spouse_name: z.string().nullable().describe("First name, middle initial, and last name of spouse (if joint return)"),
spouse_ssn: z.string().nullable().describe("Social security number of spouse (if joint return)"),
home_address: z.string().nullable().describe("Full home address including street, city, state, and ZIP code"),
virtual_currency_transaction: z.string().nullable().describe("Whether taxpayer received, sold, exchanged, or disposed of virtual currency during tax year (Yes/No)"),
dependents: z.array(
z.object({
name: z.string().nullable().describe("First and last name of dependent"),
ssn: z.string().nullable().describe("Social security number of dependent"),
relationship: z.string().nullable().describe("Relationship to taxpayer (e.g., Daughter, Son, Parent)"),
qualifies_for_child_tax_credit: z.boolean().nullable().describe("Whether dependent qualifies for child tax credit"),
})
).describe("List of dependents claimed on return"),
presidential_election_campaign_contribution: z.boolean().nullable().describe("Whether taxpayer or spouse elected to contribute $3 to Presidential Election Campaign fund"),
age_blindness_status: z.string().nullable().describe("Age/blindness status for standard deduction calculation (born before Jan 2, 1957 or blind)"),
});
type Form1040Data = z.infer<typeof form1040Schema>;
/**
* Process a U.S. Individual Income Tax Return (Form 1040) using the Extend pipeline.
*
* @param filePath - Local file path to the PDF
* @returns Extracted form data as structured JSON
*/
async function processUSIndividualIncomeTaxReturn(filePath: string): Promise<Form1040Data> {
// Convert local file to data URL (base64-encoded)
const fileBuffer = fs.readFileSync(filePath);
const base64 = fileBuffer.toString("base64");
const dataUrl = `data:application/pdf;base64,${base64}`;
console.log(`Processing Form 1040 from: ${filePath}`);
// Step 1: Parse the document to markdown with agentic OCR
console.log("Step 1: Parsing Form 1040 to markdown...");
const parseRun = await client.parseRuns.createAndPoll({
file: { url: dataUrl },
config: {
blockOptions: {
text: {
agentic: {
enabled: true,
},
},
},
chunkingStrategy: {
type: "document",
},
},
});
if (parseRun.status !== "PROCESSED") {
throw new Error(`Parse failed with status: ${parseRun.status}`);
}
// Log parsed content for debugging
const parsedContent = parseRun.output.chunks
.map((chunk) => chunk.content)
.join("\n\n");
console.log(`Parsed ${parseRun.output.chunks.length} chunks from document`);
// Step 2: Extract structured fields using the schema
console.log("Step 2: Extracting structured Form 1040 fields...");
const extractRun = await client.extractRuns.createAndPoll({
file: { url: dataUrl },
config: {
schema: form1040Schema,
baseProcessor: "extraction_performance",
advancedOptions: {
reviewAgent: {
enabled: true,
},
advancedMultimodalEnabled: true,
},
},
});
if (extractRun.status !== "PROCESSED") {
throw new Error(`Extraction failed with status: ${extractRun.status}`);
}
const extractedData = extractRun.output.value as Form1040Data;
// Validate and return extracted data
console.log("Extraction complete. Summary:");
console.log(` Tax Year: ${extractedData.tax_year}`);
console.log(` Filing Status: ${extractedData.filing_status}`);
console.log(` Primary Taxpayer: ${extractedData.primary_taxpayer_name}`);
console.log(` Dependents: ${extractedData.dependents.length}`);
return extractedData;
}
// Main entry point for testing
async function main() {
const filePath = process.argv[2] || "./form_1040.pdf";
try {
const result = await processUSIndividualIncomeTaxReturn(filePath);
console.log("\n--- Final Extracted Data ---");
console.log(JSON.stringify(result, null, 2));
} catch (error) {
console.error("Error processing Form 1040:", error);
process.exit(1);
}
}
main();import os
import json
from extend_ai import Extend
# Initialize the Extend client with API key from environment
client = Extend(token=os.environ["EXTEND_API_KEY"])
# Define the schema for Form 1040 extraction
form1040_schema = {
"type": "object",
"properties": {
"tax_year": {
"type": ["string", "null"],
"description": "The tax year of the return (e.g., 2021)"
},
"filing_status": {
"type": ["string", "null"],
"description": "Filing status selected: Single, Married filing jointly, Married filing separately, Head of household, or Qualifying widow(er)"
},
"primary_taxpayer_name": {
"type": ["string", "null"],
"description": "First name, middle initial, and last name of primary taxpayer"
},
"primary_taxpayer_ssn": {
"type": ["string", "null"],
"description": "Social security number of primary taxpayer"
},
"spouse_name": {
"type": ["string", "null"],
"description": "First name, middle initial, and last name of spouse (if joint return)"
},
"spouse_ssn": {
"type": ["string", "null"],
"description": "Social security number of spouse (if joint return)"
},
"home_address": {
"type": ["string", "null"],
"description": "Full home address including street, city, state, and ZIP code"
},
"virtual_currency_transaction": {
"type": ["string", "null"],
"description": "Whether taxpayer received, sold, exchanged, or disposed of virtual currency during tax year (Yes/No)"
},
"dependents": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": ["string", "null"],
"description": "First and last name of dependent"
},
"ssn": {
"type": ["string", "null"],
"description": "Social security number of dependent"
},
"relationship": {
"type": ["string", "null"],
"description": "Relationship to taxpayer (e.g., Daughter, Son, Parent)"
},
"qualifies_for_child_tax_credit": {
"type": ["boolean", "null"],
"description": "Whether dependent qualifies for child tax credit"
}
}
},
"description": "List of dependents claimed on return"
},
"presidential_election_campaign_contribution": {
"type": ["boolean", "null"],
"description": "Whether taxpayer or spouse elected to contribute $3 to Presidential Election Campaign fund"
},
"age_blindness_status": {
"type": ["string", "null"],
"description": "Age/blindness status for standard deduction calculation (born before Jan 2, 1957 or blind)"
}
}
}
def process_us_individual_income_tax_return(file_path: str) -> dict:
"""
Process a U.S. Individual Income Tax Return (Form 1040) using the Extend pipeline.
Args:
file_path: Local file path to the PDF
Returns:
Extracted form data as structured JSON
"""
# Convert local file to data URL (base64-encoded)
with open(file_path, "rb") as f:
file_buffer = f.read()
base64_str = __import__("base64").b64encode(file_buffer).decode("utf-8")
data_url = f"data:application/pdf;base64,{base64_str}"
print(f"Processing Form 1040 from: {file_path}")
# Step 1: Parse the document to markdown with agentic OCR
print("Step 1: Parsing Form 1040 to markdown...")
parse_run = client.parse_runs.create_and_poll(
file={"url": data_url},
config={
"blockOptions": {
"text": {
"agentic": {
"enabled": True
}
}
},
"chunkingStrategy": {
"type": "document"
}
}
)
if parse_run.status != "PROCESSED":
raise Exception(f"Parse failed with status: {parse_run.status}")
# Log parsed content for debugging
parsed_content = "\n\n".join(chunk.content for chunk in parse_run.output.chunks)
print(f"Parsed {len(parse_run.output.chunks)} chunks from document")
# Step 2: Extract structured fields using the schema
print("Step 2: Extracting structured Form 1040 fields...")
extract_run = client.extract_runs.create_and_poll(
file={"url": data_url},
config={
"schema": form1040_schema,
"baseProcessor": "extraction_performance",
"advancedOptions": {
"reviewAgent": {
"enabled": True
},
"advancedMultimodalEnabled": True
}
}
)
if extract_run.status != "PROCESSED":
raise Exception(f"Extraction failed with status: {extract_run.status}")
extracted_data = extract_run.output.value
# Validate and return extracted data
print("Extraction complete. Summary:")
print(f" Tax Year: {extracted_data.get('tax_year')}")
print(f" Filing Status: {extracted_data.get('filing_status')}")
print(f" Primary Taxpayer: {extracted_data.get('primary_taxpayer_name')}")
print(f" Dependents: {len(extracted_data.get('dependents', []))}")
return extracted_data
def main():
import sys
file_path = sys.argv[1] if len(sys.argv) > 1 else "./form_1040.pdf"
try:
result = process_us_individual_income_tax_return(file_path)
print("\n--- Final Extracted Data ---")
print(json.dumps(result, indent=2))
except Exception as error:
print(f"Error processing Form 1040: {error}")
sys.exit(1)
if __name__ == "__main__":
main()// This code uses the Extend REST API directly because Extend has no official Java SDK yet.
// It calls https://api.extend.ai endpoints with java.net.http.HttpClient (no external dependencies).
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Scanner;
public class Form1040Processor {
private static final String API_BASE_URL = "https://api.extend.ai";
private static final String API_KEY = System.getenv("EXTEND_API_KEY");
private static final HttpClient httpClient = HttpClient.newHttpClient();
/**
* Process a U.S. Individual Income Tax Return (Form 1040) using the Extend pipeline.
*
* @param filePath - Local file path to the PDF
* @return Extracted form data as JSON string
*/
public static String processUSIndividualIncomeTaxReturn(String filePath) throws IOException, InterruptedException {
// Convert local file to data URL (base64-encoded)
byte[] fileBytes = Files.readAllBytes(Paths.get(filePath));
String base64 = Base64.getEncoder().encodeToString(fileBytes);
String dataUrl = "data:application/pdf;base64," + base64;
System.out.println("Processing Form 1040 from: " + filePath);
// Step 1: Parse the document to markdown with agentic OCR
System.out.println("Step 1: Parsing Form 1040 to markdown...");
String parseRequestBody = buildParseRequestBody(dataUrl);
String parseRunId = createAndPollParseRun(parseRequestBody);
if (parseRunId == null) {
throw new RuntimeException("Parse failed or did not complete");
}
System.out.println("Parse completed with run ID: " + parseRunId);
// Step 2: Extract structured fields using the schema
System.out.println("Step 2: Extracting structured Form 1040 fields...");
String extractRequestBody = buildExtractRequestBody(dataUrl);
String extractedData = createAndPollExtractRun(extractRequestBody);
if (extractedData == null) {
throw new RuntimeException("Extraction failed or did not complete");
}
System.out.println("Extraction complete.");
return extractedData;
}
private static String buildParseRequestBody(String dataUrl) {
return "{"
+ "\"file\":{\"url\":\"" + escapeJson(dataUrl) + "\"},"
+ "\"config\":{"
+ "\"blockOptions\":{\"text\":{\"agentic\":{\"enabled\":true}}},"
+ "\"chunkingStrategy\":{\"type\":\"document\"}"
+ "}"
+ "}";
}
private static String buildExtractRequestBody(String dataUrl) {
String schema = "{"
+ "\"type\":\"object\","
+ "\"properties\":{"
+ "\"tax_year\":{\"type\":[\"string\",\"null\"],\"description\":\"The tax year of the return (e.g., 2021)\"},"
+ "\"filing_status\":{\"type\":[\"string\",\"null\"],\"description\":\"Filing status selected: Single, Married filing jointly, Married filing separately, Head of household, or Qualifying widow(er)\"},"
+ "\"primary_taxpayer_name\":{\"type\":[\"string\",\"null\"],\"description\":\"First name, middle initial, and last name of primary taxpayer\"},"
+ "\"primary_taxpayer_ssn\":{\"type\":[\"string\",\"null\"],\"description\":\"Social security number of primary taxpayer\"},"
+ "\"spouse_name\":{\"type\":[\"string\",\"null\"],\"description\":\"First name, middle initial, and last name of spouse (if joint return)\"},"
+ "\"spouse_ssn\":{\"type\":[\"string\",\"null\"],\"description\":\"Social security number of spouse (if joint return)\"},"
+ "\"home_address\":{\"type\":[\"string\",\"null\"],\"description\":\"Full home address including street, city, state, and ZIP code\"},"
+ "\"virtual_currency_transaction\":{\"type\":[\"string\",\"null\"],\"description\":\"Whether taxpayer received, sold, exchanged, or disposed of virtual currency during tax year (Yes/No)\"},"
+ "\"dependents\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":[\"string\",\"null\"],\"description\":\"First and last name of dependent\"},\"ssn\":{\"type\":[\"string\",\"null\"],\"description\":\"Social security number of dependent\"},\"relationship\":{\"type\":[\"string\",\"null\"],\"description\":\"Relationship to taxpayer (e.g., Daughter, Son, Parent)\"},\"qualifies_for_child_tax_credit\":{\"type\":[\"boolean\",\"null\"],\"description\":\"Whether dependent qualifies for child tax credit\"}}},\"description\":\"List of dependents claimed on return\"},"
+ "\"presidential_election_campaign_contribution\":{\"type\":[\"boolean\",\"null\"],\"description\":\"Whether taxpayer or spouse elected to contribute $3 to Presidential Election Campaign fund\"},"
+ "\"age_blindness_status\":{\"type\":[\"string\",\"null\"],\"description\":\"Age/blindness status for standard deduction calculation (born before Jan 2, 1957 or blind)\"}"
+ "}"
+ "}";
return "{"
+ "\"file\":{\"url\":\"" + escapeJson(dataUrl) + "\"},"
+ "\"config\":{"
+ "\"schema\":" + schema + ","
+ "\"baseProcessor\":\"extraction_performance\","
+ "\"advancedOptions\":{"
+ "\"reviewAgent\":{\"enabled\":true},"
+ "\"advancedMultimodalEnabled\":true"
+ "}"
+ "}"
+ "}";
}
private static String createAndPollParseRun(String requestBody) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(API_BASE_URL + "/v1/parse_runs"))
.header("Authorization", "Bearer " + API_KEY)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200 && response.statusCode() != 201) {
System.err.println("Parse request failed: " + response.statusCode());
System.err.println(response.body());
return null;
}
String responseBody = response.body();
String runId = extractFieldFromJson(responseBody, "id");
if (runId == null) {
return null;
}
// Poll for completion
return pollForCompletion("/v1/parse_runs/" + runId);
}
private static String createAndPollExtractRun(String requestBody) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(API_BASE_URL + "/v1/extract_runs"))
.header("Authorization", "Bearer " + API_KEY)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200 && response.statusCode() != 201) {
System.err.println("Extract request failed: " + response.statusCode());
System.err.println(response.body());
return null;
}
String responseBody = response.body();
String runId = extractFieldFromJson(responseBody, "id");
if (runId == null) {
return null;
}
// Poll for completion and return output
return pollForExtractionCompletion("/v1/extract_runs/" + runId);
}
private static String pollForCompletion(String endpoint) throws IOException, InterruptedException {
int maxAttempts = 120;
int attempt = 0;
while (attempt < maxAttempts) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(API_BASE_URL + endpoint))
.header("Authorization", "Bearer " + API_KEY)
.GET()
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
String status = extractFieldFromJson(response.body(), "status");
if ("PROCESSED".equals(status)) {
return extractFieldFromJson(response.body(), "id");
}
}
Thread.sleep(1000);
attempt++;
}
return null;
}
private static String pollForExtractionCompletion(String endpoint) throws IOException, InterruptedException {
int maxAttempts = 120;
int attempt = 0;
while (attempt < maxAttempts) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(API_BASE_URL + endpoint))
.header("Authorization", "Bearer " + API_KEY)
.GET()
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
String status = extractFieldFromJson(response.body(), "status");
if ("PROCESSED".equals(status)) {
return extractFieldFromJson(response.body(), "output");
}
}
Thread.sleep(1000);
attempt++;
}
return null;
}
private static String extractFieldFromJson(String json, String fieldName) {
String searchKey = "\"" + fieldName + "\":";
int startIndex = json.indexOf(searchKey);
if (startIndex == -1) {
return null;
}
startIndex += searchKey.length();
while (startIndex < json.length() && Character.isWhitespace(json.charAt(startIndex))) {
startIndex++;
}
if (startIndex >= json.length()) {
return null;
}
if (json.charAt(startIndex) == '"') {
startIndex++;
int endIndex = json.indexOf('"', startIndex);
if (endIndex != -1) {
return json.substring(startIndex, endIndex);
}
} else if (json.charAt(startIndex) == '{') {
int braceCount = 1;
int endIndex = startIndex + 1;
while (endIndex < json.length() && braceCount > 0) {
if (json.charAt(endIndex) == '{') braceCount++;
else if (json.charAt(endIndex) == '}') braceCount--;
endIndex++;
}
return json.substring(startIndex, endIndex);
}
return null;
}
private static String escapeJson(String input) {
return input.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t");
}
public static void main(String[] args) {
String filePath = args.length > 0 ? args[0] : "./form_1040.pdf";
try {
String result = processUSIndividualIncomeTaxReturn(filePath);
System.out.println("\n--- Final Extracted Data ---");
System.out.println(result);
} catch (Exception e) {
System.err.println("Error processing Form 1040: " + e.getMessage());
e.printStackTrace();
System.exit(1);
}
}
}// This code uses the Extend REST API directly because Extend has no official Go SDK yet.
// It calls https://api.extend.ai endpoints with standard net/http and encoding/json.
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
// Dependent represents a dependent claimed on the return
type Dependent struct {
Name *string `json:"name"`
SSN *string `json:"ssn"`
Relationship *string `json:"relationship"`
QualifiesForChildTaxCredit *bool `json:"qualifies_for_child_tax_credit"`
}
// Form1040Data represents the extracted Form 1040 data
type Form1040Data struct {
TaxYear *string `json:"tax_year"`
FilingStatus *string `json:"filing_status"`
PrimaryTaxpayerName *string `json:"primary_taxpayer_name"`
PrimaryTaxpayerSSN *string `json:"primary_taxpayer_ssn"`
SpouseName *string `json:"spouse_name"`
SpouseSSN *string `json:"spouse_ssn"`
HomeAddress *string `json:"home_address"`
VirtualCurrencyTransaction *string `json:"virtual_currency_transaction"`
Dependents []Dependent `json:"dependents"`
PresidentialElectionCampaignContribution *bool `json:"presidential_election_campaign_contribution"`
AgeBlindsStatus *string `json:"age_blindness_status"`
}
// ParseRunOutput represents the output from a parse run
type ParseRunOutput struct {
Chunks []struct {
Content string `json:"content"`
} `json:"chunks"`
}
// ParseRun represents a parse run response
type ParseRun struct {
Status string `json:"status"`
Output ParseRunOutput `json:"output"`
}
// ExtractRunOutput represents the output from an extract run
type ExtractRunOutput struct {
Value Form1040Data `json:"value"`
}
// ExtractRun represents an extract run response
type ExtractRun struct {
Status string `json:"status"`
Output ExtractRunOutput `json:"output"`
}
// ExtendClient wraps HTTP calls to the Extend API
type ExtendClient struct {
token string
baseURL string
httpClient *http.Client
}
// NewExtendClient creates a new Extend API client
func NewExtendClient(token string) *ExtendClient {
return &ExtendClient{
token: token,
baseURL: "https://api.extend.ai",
httpClient: &http.Client{
Timeout: 5 * time.Minute,
},
}
}
// doRequest performs an HTTP request to the Extend API
func (c *ExtendClient) doRequest(method, path string, body interface{}) ([]byte, error) {
url := c.baseURL + path
var reqBody io.Reader
if body != nil {
jsonBody, err := json.Marshal(body)
if err != nil {
return nil, err
}
reqBody = bytes.NewReader(jsonBody)
}
req, err := http.NewRequest(method, url, reqBody)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.token))
req.Header.Set("Content-Type", "application/json")
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("API error: status %d, body: %s", resp.StatusCode, string(respBody))
}
return respBody, nil
}
// CreateParseRun initiates a parse run
func (c *ExtendClient) CreateParseRun(dataURL string) (string, error) {
payload := map[string]interface{}{
"file": map[string]string{
"url": dataURL,
},
"config": map[string]interface{}{
"blockOptions": map[string]interface{}{
"text": map[string]interface{}{
"agentic": map[string]bool{
"enabled": true,
},
},
},
"chunkingStrategy": map[string]string{
"type": "document",
},
},
}
respBody, err := c.doRequest("POST", "/v1/parseRuns", payload)
if err != nil {
return "", err
}
var result map[string]interface{}
if err := json.Unmarshal(respBody, &result); err != nil {
return "", err
}
runID, ok := result["id"].(string)
if !ok {
return "", fmt.Errorf("no run ID in response")
}
return runID, nil
}
// GetParseRun polls a parse run until completion
func (c *ExtendClient) GetParseRun(runID string) (*ParseRun, error) {
respBody, err := c.doRequest("GET", fmt.Sprintf("/v1/parseRuns/%s", runID), nil)
if err != nil {
return nil, err
}
var run ParseRun
if err := json.Unmarshal(respBody, &run); err != nil {
return nil, err
}
return &run, nil
}
// PollParseRun polls a parse run until it completes
func (c *ExtendClient) PollParseRun(runID string) (*ParseRun, error) {
for {
run, err := c.GetParseRun(runID)
if err != nil {
return nil, err
}
if run.Status == "PROCESSED" || run.Status == "FAILED" {
return run, nil
}
time.Sleep(2 * time.Second)
}
}
// CreateExtractRun initiates an extract run
func (c *ExtendClient) CreateExtractRun(dataURL string) (string, error) {
schema := map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"tax_year": map[string]interface{}{
"type": []string{"string", "null"},
"description": "The tax year of the return (e.g., 2021)",
},
"filing_status": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Filing status selected: Single, Married filing jointly, Married filing separately, Head of household, or Qualifying widow(er)",
},
"primary_taxpayer_name": map[string]interface{}{
"type": []string{"string", "null"},
"description": "First name, middle initial, and last name of primary taxpayer",
},
"primary_taxpayer_ssn": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Social security number of primary taxpayer",
},
"spouse_name": map[string]interface{}{
"type": []string{"string", "null"},
"description": "First name, middle initial, and last name of spouse (if joint return)",
},
"spouse_ssn": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Social security number of spouse (if joint return)",
},
"home_address": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Full home address including street, city, state, and ZIP code",
},
"virtual_currency_transaction": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Whether taxpayer received, sold, exchanged, or disposed of virtual currency during tax year (Yes/No)",
},
"dependents": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"name": map[string]interface{}{
"type": []string{"string", "null"},
"description": "First and last name of dependent",
},
"ssn": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Social security number of dependent",
},
"relationship": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Relationship to taxpayer (e.g., Daughter, Son, Parent)",
},
"qualifies_for_child_tax_credit": map[string]interface{}{
"type": []interface{}{true, nil},
"description": "Whether dependent qualifies for child tax credit",
},
},
},
"description": "List of dependents claimed on return",
},
"presidential_election_campaign_contribution": map[string]interface{}{
"type": []interface{}{true, nil},
"description": "Whether taxpayer or spouse elected to contribute $3 to Presidential Election Campaign fund",
},
"age_blindness_status": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Age/blindness status for standard deduction calculation (born before Jan 2, 1957 or blind)",
},
},
}
payload := map[string]interface{}{
"file": map[string]string{
"url": dataURL,
},
"config": map[string]interface{}{
"schema": schema,
"baseProcessor": "extraction_performance",
"advancedOptions": map[string]interface{}{
"reviewAgent": map[string]bool{
"enabled": true,
},
"advancedMultimodalEnabled": true,
},
},
}
respBody, err := c.doRequest("POST", "/v1/extractRuns", payload)
if err != nil {
return "", err
}
var result map[string]interface{}
if err := json.Unmarshal(respBody, &result); err != nil {
return "", err
}
runID, ok := result["id"].(string)
if !ok {
return "", fmt.Errorf("no run ID in response")
}
return runID, nil
}
// GetExtractRun retrieves an extract run
func (c *ExtendClient) GetExtractRun(runID string) (*ExtractRun, error) {
respBody, err := c.doRequest("GET", fmt.Sprintf("/v1/extractRuns/%s", runID), nil)
if err != nil {
return nil, err
}
var run ExtractRun
if err := json.Unmarshal(respBody, &run); err != nil {
return nil, err
}
return &run, nil
}
// PollExtractRun polls an extract run until it completes
func (c *ExtendClient) PollExtractRun(runID string) (*ExtractRun, error) {
for {
run, err := c.GetExtractRun(runID)
if err != nil {
return nil, err
}
if run.Status == "PROCESSED" || run.Status == "FAILED" {
return run, nil
}
time.Sleep(2 * time.Second)
}
}
// ProcessUSIndividualIncomeTaxReturn processes a Form 1040 PDF and extracts structured data
func ProcessUSIndividualIncomeTaxReturn(filePath string, apiKey string) (*Form1040Data, error) {
// Read file and convert to base64 data URL
fileBuffer, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
base64Str := base64.StdEncoding.EncodeToString(fileBuffer)
dataURL := fmt.Sprintf("data:application/pdf;base64,%s", base64Str)
client := NewExtendClient(apiKey)
fmt.Printf("Processing Form 1040 from: %s\n", filePath)
// Step 1: Parse the document to markdown with agentic OCR
fmt.Println("Step 1: Parsing Form 1040 to markdown...")
parseRunID, err := client.CreateParseRun(dataURL)
if err != nil {
return nil, err
}
parseRun, err := client.PollParseRun(parseRunID)
if err != nil {
return nil, err
}
if parseRun.Status != "PROCESSED" {
return nil, fmt.Errorf("parse failed with status: %s", parseRun.Status)
}
fmt.Printf("Parsed %d chunks from document\n", len(parseRun.Output.Chunks))
// Step 2: Extract structured fields using the schema
fmt.Println("Step 2: Extracting structured Form 1040 fields...")
extractRunID, err := client.CreateExtractRun(dataURL)
if err != nil {
return nil, err
}
extractRun, err := client.PollExtractRun(extractRunID)
if err != nil {
return nil, err
}
if extractRun.Status != "PROCESSED" {
return nil, fmt.Errorf("extraction failed with status: %s", extractRun.Status)
}
extractedData := &extractRun.Output.Value
// Log summary
fmt.Println("Extraction complete. Summary:")
if extractedData.TaxYear != nil {
fmt.Printf(" Tax Year: %s\n", *extractedData.TaxYear)
}
if extractedData.FilingStatus != nil {
fmt.Printf(" Filing Status: %s\n", *extractedData.FilingStatus)
}
if extractedData.PrimaryTaxpayerName != nil {
fmt.Printf(" Primary Taxpayer: %s\n", *extractedData.PrimaryTaxpayerName)
}
fmt.Printf(" Dependents: %d\n", len(extractedData.Dependents))
return extractedData, nil
}
func main() {
filePath := "./form_1040.pdf"
if len(os.Args) > 1 {
filePath = os.Args[1]
}
apiKey := os.Getenv("EXTEND_API_KEY")
if apiKey == "" {
fmt.Fprintf(os.Stderr, "Error: EXTEND_API_KEY environment variable not set\n")
os.Exit(1)
}
result, err := ProcessUSIndividualIncomeTaxReturn(filePath, apiKey)
if err != nil {
fmt.Fprintf(os.Stderr, "Error processing Form 1040: %v\n", err)
os.Exit(1)
}
fmt.Println("\n--- Final Extracted Data ---")
jsonBytes, err := json.MarshalIndent(result, "", " ")
if err != nil {
fmt.Fprintf(os.Stderr, "Error marshaling JSON: %v\n", err)
os.Exit(1)
}
fmt.Println(string(jsonBytes))
}// Deploy the "U.S. Individual Income Tax Return" pipeline to YOUR Extend account.
//
// The workflow below is fully self-contained — every EXTRACT/CLASSIFY/SPLIT
// step carries its extractor/classifier/splitter config INLINE, so this is a
// single API call. No processors to create or wire up beforehand.
// Idempotent: the created workflow id is cached in .extend/1040-extractor.json,
// so re-running updates the existing workflow instead of duplicating it.
//
// Usage:
// export EXTEND_API_KEY=sk_... (from https://dashboard.extend.ai → API Keys)
// npx tsx provision.ts
//
// Generated by doc1 (template: 1040-extractor).
import fs from "node:fs";
import path from "node:path";
const API = "https://api.extend.ai";
const VERSION = "2026-02-09";
const API_KEY = process.env.EXTEND_API_KEY;
if (!API_KEY) { console.error("Set EXTEND_API_KEY first."); process.exit(1); }
const STATE_DIR = path.join(process.cwd(), ".extend");
const STATE_FILE = path.join(STATE_DIR, "1040-extractor.json");
type State = { workflowId?: string };
const state: State = fs.existsSync(STATE_FILE)
? JSON.parse(fs.readFileSync(STATE_FILE, "utf8"))
: {};
function saveState() {
fs.mkdirSync(STATE_DIR, { recursive: true });
fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
}
async function api(method: string, pathName: string, body?: unknown) {
const res = await fetch(API + pathName, {
method,
headers: {
Authorization: `Bearer ${API_KEY}`,
"x-extend-api-version": VERSION,
...(body ? { "Content-Type": "application/json" } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(`${method} ${pathName} failed (${res.status}): ${JSON.stringify(data).slice(0, 300)}`);
return data;
}
// ── Workflow definition — extractor/classifier/splitter configs inline ──────
const WORKFLOW = {
"name": "U.S. Individual Income Tax Return Processing Pipeline",
"steps": [
{
"name": "startTrigger1",
"type": "TRIGGER",
"next": [
{
"step": "parse1"
}
]
},
{
"name": "parse1",
"type": "PARSE",
"config": {
"parseConfig": {
"blockOptions": {
"text": {
"agentic": {
"enabled": true
}
}
},
"chunkingStrategy": {
"type": "document"
}
}
},
"next": [
{
"step": "extraction2"
}
]
},
{
"name": "extraction2",
"type": "EXTRACT",
"config": {
"extractorConfig": {
"schema": {
"type": "object",
"properties": {
"tax_year": {
"type": [
"string",
"null"
],
"description": "The tax year of the return (e.g., 2021)"
},
"dependents": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ssn": {
"type": [
"string",
"null"
],
"description": "Social security number of dependent"
},
"name": {
"type": [
"string",
"null"
],
"description": "First and last name of dependent"
},
"relationship": {
"type": [
"string",
"null"
],
"description": "Relationship to taxpayer (e.g., Daughter, Son, Parent)"
},
"qualifies_for_child_tax_credit": {
"type": [
"boolean",
"null"
],
"description": "Whether dependent qualifies for child tax credit"
}
}
},
"description": "List of dependents claimed on return"
},
"spouse_ssn": {
"type": [
"string",
"null"
],
"description": "Social security number of spouse (if joint return)"
},
"spouse_name": {
"type": [
"string",
"null"
],
"description": "First name, middle initial, and last name of spouse (if joint return)"
},
"home_address": {
"type": [
"string",
"null"
],
"description": "Full home address including street, city, state, and ZIP code"
},
"filing_status": {
"type": [
"string",
"null"
],
"description": "Filing status selected: Single, Married filing jointly, Married filing separately, Head of household, or Qualifying widow(er)"
},
"age_blindness_status": {
"type": [
"string",
"null"
],
"description": "Age/blindness status for standard deduction calculation (born before Jan 2, 1957 or blind)"
},
"primary_taxpayer_ssn": {
"type": [
"string",
"null"
],
"description": "Social security number of primary taxpayer"
},
"primary_taxpayer_name": {
"type": [
"string",
"null"
],
"description": "First name, middle initial, and last name of primary taxpayer"
},
"virtual_currency_transaction": {
"type": [
"string",
"null"
],
"description": "Whether taxpayer received, sold, exchanged, or disposed of virtual currency during tax year (Yes/No)"
},
"presidential_election_campaign_contribution": {
"type": [
"boolean",
"null"
],
"description": "Whether taxpayer or spouse elected to contribute $3 to Presidential Election Campaign fund"
}
}
},
"baseProcessor": "extraction_performance",
"advancedOptions": {
"reviewAgent": {
"enabled": true
},
"advancedMultimodalEnabled": true
}
}
}
}
]
};
async function main() {
console.log(`Deploying "${WORKFLOW.name}"…`);
if (state.workflowId) {
console.log(`✓ workflow already provisioned (${state.workflowId}) — updating steps`);
await api("POST", `/workflows/${state.workflowId}`, { steps: WORKFLOW.steps });
} else {
// Reuse an existing workflow with the same name if one exists (e.g. a
// previous run's state file was lost) instead of creating a duplicate.
try {
const list = await api("GET", `/workflows?name=${encodeURIComponent(WORKFLOW.name)}`);
const items = (list.data ?? list.items ?? []) as Array<{ name?: string; id?: string }>;
const existing = items.find((x) => x.name === WORKFLOW.name);
if (existing?.id) {
state.workflowId = existing.id; saveState();
console.log(`✓ workflow "${WORKFLOW.name}" found in your account (${existing.id}) — updating steps`);
await api("POST", `/workflows/${existing.id}`, { steps: WORKFLOW.steps });
}
} catch { /* lookup is best-effort; fall through to create */ }
if (!state.workflowId) {
const created = await api("POST", "/workflows", WORKFLOW);
const wfId = created.id ?? created.workflow?.id;
if (!wfId) throw new Error("Could not read created workflow id from response");
state.workflowId = wfId; saveState();
console.log(`+ created workflow (${wfId})`);
}
}
// Deploy the current draft as a new version so the workflow is runnable —
// best-effort: some accounts/plans may not require this explicit step.
await api("POST", `/workflows/${state.workflowId}/versions`, {}).catch(() => {});
console.log("\nDone. Run documents through it with:");
console.log(` POST ${API}/workflow_runs { workflow: { id: "${state.workflowId}" }, file: { url: "https://…" } }`);
console.log("Or open the workflow in the Extend dashboard to review and deploy it.");
}
main().catch((e) => { console.error(e.message ?? e); process.exit(1); });
import json
import os
import sys
from pathlib import Path
from extend_ai import Extend
API_KEY = os.environ.get("EXTEND_API_KEY")
if not API_KEY:
print("Set EXTEND_API_KEY first.", file=sys.stderr)
sys.exit(1)
STATE_DIR = Path.cwd() / ".extend"
STATE_FILE = STATE_DIR / "1040-extractor.json"
state = {}
if STATE_FILE.exists():
state = json.loads(STATE_FILE.read_text())
def save_state():
STATE_DIR.mkdir(parents=True, exist_ok=True)
STATE_FILE.write_text(json.dumps(state, indent=2))
WORKFLOW = {
"name": "U.S. Individual Income Tax Return Processing Pipeline",
"steps": [
{
"name": "startTrigger1",
"type": "TRIGGER",
"next": [{"step": "parse1"}],
},
{
"name": "parse1",
"type": "PARSE",
"config": {
"parseConfig": {
"blockOptions": {"text": {"agentic": {"enabled": True}}},
"chunkingStrategy": {"type": "document"},
}
},
"next": [{"step": "extraction2"}],
},
{
"name": "extraction2",
"type": "EXTRACT",
"config": {
"extractorConfig": {
"schema": {
"type": "object",
"properties": {
"tax_year": {
"type": ["string", "null"],
"description": "The tax year of the return (e.g., 2021)",
},
"dependents": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ssn": {
"type": ["string", "null"],
"description": "Social security number of dependent",
},
"name": {
"type": ["string", "null"],
"description": "First and last name of dependent",
},
"relationship": {
"type": ["string", "null"],
"description": "Relationship to taxpayer (e.g., Daughter, Son, Parent)",
},
"qualifies_for_child_tax_credit": {
"type": ["boolean", "null"],
"description": "Whether dependent qualifies for child tax credit",
},
},
},
"description": "List of dependents claimed on return",
},
"spouse_ssn": {
"type": ["string", "null"],
"description": "Social security number of spouse (if joint return)",
},
"spouse_name": {
"type": ["string", "null"],
"description": "First name, middle initial, and last name of spouse (if joint return)",
},
"home_address": {
"type": ["string", "null"],
"description": "Full home address including street, city, state, and ZIP code",
},
"filing_status": {
"type": ["string", "null"],
"description": "Filing status selected: Single, Married filing jointly, Married filing separately, Head of household, or Qualifying widow(er)",
},
"age_blindness_status": {
"type": ["string", "null"],
"description": "Age/blindness status for standard deduction calculation (born before Jan 2, 1957 or blind)",
},
"primary_taxpayer_ssn": {
"type": ["string", "null"],
"description": "Social security number of primary taxpayer",
},
"primary_taxpayer_name": {
"type": ["string", "null"],
"description": "First name, middle initial, and last name of primary taxpayer",
},
"virtual_currency_transaction": {
"type": ["string", "null"],
"description": "Whether taxpayer received, sold, exchanged, or disposed of virtual currency during tax year (Yes/No)",
},
"presidential_election_campaign_contribution": {
"type": ["boolean", "null"],
"description": "Whether taxpayer or spouse elected to contribute $3 to Presidential Election Campaign fund",
},
},
},
"baseProcessor": "extraction_performance",
"advancedOptions": {
"reviewAgent": {"enabled": True},
"advancedMultimodalEnabled": True,
},
}
},
},
],
}
def main():
client = Extend(token=API_KEY)
print(f'Deploying "{WORKFLOW["name"]}…"')
if state.get("workflowId"):
workflow_id = state["workflowId"]
print(f"✓ workflow already provisioned ({workflow_id}) — updating steps")
client.workflows.update(workflow_id, steps=WORKFLOW["steps"])
else:
# Try to find an existing workflow with the same name
try:
workflows_list = client.workflows.list(name=WORKFLOW["name"])
items = workflows_list.data if hasattr(workflows_list, "data") else []
existing = next(
(w for w in items if w.name == WORKFLOW["name"]), None
)
if existing and existing.id:
state["workflowId"] = existing.id
save_state()
print(
f'✓ workflow "{WORKFLOW["name"]}" found in your account ({existing.id}) — updating steps'
)
client.workflows.update(existing.id, steps=WORKFLOW["steps"])
except Exception:
# Lookup is best-effort; fall through to create
pass
if not state.get("workflowId"):
created = client.workflows.create(**WORKFLOW)
workflow_id = created.id
if not workflow_id:
raise ValueError("Could not read created workflow id from response")
state["workflowId"] = workflow_id
save_state()
print(f"+ created workflow ({workflow_id})")
# Deploy the current draft as a new version so the workflow is runnable
try:
client.workflows.create_version(state["workflowId"])
except Exception:
# Best-effort: some accounts/plans may not require this explicit step
pass
print("\nDone. Run documents through it with:")
print(
f' POST https://api.extend.ai/workflow_runs {{ "workflow": {{ "id": "{state["workflowId"]}" }}, "file": {{ "url": "https://…" }} }}'
)
print("Or open the workflow in the Extend dashboard to review and deploy it.")
if __name__ == "__main__":
try:
main()
except Exception as e:
print(str(e), file=sys.stderr)
sys.exit(1)// This code uses the Extend REST API directly because Extend has no official Java SDK yet.
// It deploys the "U.S. Individual Income Tax Return" pipeline to your Extend account.
//
// Usage:
// export EXTEND_API_KEY=sk_... (from https://dashboard.extend.ai → API Keys)
// javac Provision.java && java Provision
//
// Generated by doc1 (template: 1040-extractor).
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class Provision {
private static final String API = "https://api.extend.ai";
private static final String VERSION = "2026-02-09";
private static final String API_KEY = System.getenv("EXTEND_API_KEY");
private static final Path STATE_DIR = Paths.get(System.getProperty("user.dir"), ".extend");
private static final Path STATE_FILE = STATE_DIR.resolve("1040-extractor.json");
private static final HttpClient HTTP = HttpClient.newHttpClient();
static {
if (API_KEY == null || API_KEY.isEmpty()) {
System.err.println("Set EXTEND_API_KEY first.");
System.exit(1);
}
}
static class State {
String workflowId;
}
private static State state = new State();
static {
try {
if (Files.exists(STATE_FILE)) {
String json = Files.readString(STATE_FILE);
state.workflowId = parseWorkflowId(json);
}
} catch (IOException e) {
// Ignore; state starts empty
}
}
private static void saveState() throws IOException {
Files.createDirectories(STATE_DIR);
String json = String.format("{\"workflowId\": \"%s\"}", state.workflowId);
Files.writeString(STATE_FILE, json);
}
private static String parseWorkflowId(String json) {
int idx = json.indexOf("\"workflowId\"");
if (idx == -1) return null;
int start = json.indexOf("\"", idx + 12) + 1;
int end = json.indexOf("\"", start);
return json.substring(start, end);
}
private static Map<String, Object> api(String method, String pathName, Object body)
throws IOException, InterruptedException {
HttpRequest.Builder builder = HttpRequest.newBuilder()
.uri(URI.create(API + pathName))
.header("Authorization", "Bearer " + API_KEY)
.header("x-extend-api-version", VERSION);
if (body != null) {
String jsonBody = toJson(body);
builder.header("Content-Type", "application/json")
.method(method, HttpRequest.BodyPublishers.ofString(jsonBody));
} else {
builder.method(method, HttpRequest.BodyPublishers.noBody());
}
HttpRequest request = builder.build();
HttpResponse<String> response = HTTP.send(request, HttpResponse.BodyHandlers.ofString());
Map<String, Object> data = parseJson(response.body());
if (response.statusCode() < 200 || response.statusCode() >= 300) {
String msg = toJson(data);
if (msg.length() > 300) msg = msg.substring(0, 300);
throw new RuntimeException(method + " " + pathName + " failed (" + response.statusCode() + "): " + msg);
}
return data;
}
private static String toJson(Object obj) {
if (obj == null) return "null";
if (obj instanceof String) return "\"" + escapeJson((String) obj) + "\"";
if (obj instanceof Boolean || obj instanceof Number) return obj.toString();
if (obj instanceof Map) {
Map<?, ?> map = (Map<?, ?>) obj;
StringBuilder sb = new StringBuilder("{");
boolean first = true;
for (Map.Entry<?, ?> e : map.entrySet()) {
if (!first) sb.append(",");
sb.append("\"").append(e.getKey()).append("\":").append(toJson(e.getValue()));
first = false;
}
sb.append("}");
return sb.toString();
}
if (obj instanceof List) {
List<?> list = (List<?>) obj;
StringBuilder sb = new StringBuilder("[");
boolean first = true;
for (Object item : list) {
if (!first) sb.append(",");
sb.append(toJson(item));
first = false;
}
sb.append("]");
return sb.toString();
}
return obj.toString();
}
private static String escapeJson(String s) {
return s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r");
}
private static Map<String, Object> parseJson(String json) {
Map<String, Object> result = new LinkedHashMap<>();
json = json.trim();
if (!json.startsWith("{")) return result;
json = json.substring(1, json.length() - 1);
int depth = 0;
int start = 0;
for (int i = 0; i < json.length(); i++) {
char c = json.charAt(i);
if (c == '{' || c == '[') depth++;
else if (c == '}' || c == ']') depth--;
else if (c == ',' && depth == 0) {
parsePair(json.substring(start, i).trim(), result);
start = i + 1;
}
}
if (start < json.length()) parsePair(json.substring(start).trim(), result);
return result;
}
private static void parsePair(String pair, Map<String, Object> map) {
int colon = pair.indexOf(':');
if (colon == -1) return;
String key = pair.substring(0, colon).trim();
if (key.startsWith("\"")) key = key.substring(1, key.length() - 1);
String value = pair.substring(colon + 1).trim();
map.put(key, value);
}
private static Map<String, Object> buildWorkflow() {
Map<String, Object> workflow = new LinkedHashMap<>();
workflow.put("name", "U.S. Individual Income Tax Return Processing Pipeline");
List<Map<String, Object>> steps = List.of(
buildTriggerStep(),
buildParseStep(),
buildExtractionStep()
);
workflow.put("steps", steps);
return workflow;
}
private static Map<String, Object> buildTriggerStep() {
Map<String, Object> step = new LinkedHashMap<>();
step.put("name", "startTrigger1");
step.put("type", "TRIGGER");
step.put("next", List.of(Map.of("step", "parse1")));
return step;
}
private static Map<String, Object> buildParseStep() {
Map<String, Object> step = new LinkedHashMap<>();
step.put("name", "parse1");
step.put("type", "PARSE");
Map<String, Object> config = new LinkedHashMap<>();
Map<String, Object> parseConfig = new LinkedHashMap<>();
Map<String, Object> blockOptions = new LinkedHashMap<>();
Map<String, Object> text = new LinkedHashMap<>();
Map<String, Object> agentic = new LinkedHashMap<>();
agentic.put("enabled", true);
text.put("agentic", agentic);
blockOptions.put("text", text);
parseConfig.put("blockOptions", blockOptions);
parseConfig.put("chunkingStrategy", Map.of("type", "document"));
config.put("parseConfig", parseConfig);
step.put("config", config);
step.put("next", List.of(Map.of("step", "extraction2")));
return step;
}
private static Map<String, Object> buildExtractionStep() {
Map<String, Object> step = new LinkedHashMap<>();
step.put("name", "extraction2");
step.put("type", "EXTRACT");
Map<String, Object> config = new LinkedHashMap<>();
Map<String, Object> extractorConfig = new LinkedHashMap<>();
extractorConfig.put("schema", buildSchema());
extractorConfig.put("baseProcessor", "extraction_performance");
Map<String, Object> advancedOptions = new LinkedHashMap<>();
advancedOptions.put("reviewAgent", Map.of("enabled", true));
advancedOptions.put("advancedMultimodalEnabled", true);
extractorConfig.put("advancedOptions", advancedOptions);
config.put("extractorConfig", extractorConfig);
step.put("config", config);
return step;
}
private static Map<String, Object> buildSchema() {
Map<String, Object> schema = new LinkedHashMap<>();
schema.put("type", "object");
Map<String, Object> properties = new LinkedHashMap<>();
properties.put("tax_year", Map.of(
"type", List.of("string", "null"),
"description", "The tax year of the return (e.g., 2021)"
));
Map<String, Object> dependentItem = new LinkedHashMap<>();
dependentItem.put("type", "object");
Map<String, Object> dependentProps = new LinkedHashMap<>();
dependentProps.put("ssn", Map.of("type", List.of("string", "null"), "description", "Social security number of dependent"));
dependentProps.put("name", Map.of("type", List.of("string", "null"), "description", "First and last name of dependent"));
dependentProps.put("relationship", Map.of("type", List.of("string", "null"), "description", "Relationship to taxpayer (e.g., Daughter, Son, Parent)"));
dependentProps.put("qualifies_for_child_tax_credit", Map.of("type", List.of("boolean", "null"), "description", "Whether dependent qualifies for child tax credit"));
dependentItem.put("properties", dependentProps);
properties.put("dependents", Map.of(
"type", "array",
"items", dependentItem,
"description", "List of dependents claimed on return"
));
properties.put("spouse_ssn", Map.of("type", List.of("string", "null"), "description", "Social security number of spouse (if joint return)"));
properties.put("spouse_name", Map.of("type", List.of("string", "null"), "description", "First name, middle initial, and last name of spouse (if joint return)"));
properties.put("home_address", Map.of("type", List.of("string", "null"), "description", "Full home address including street, city, state, and ZIP code"));
properties.put("filing_status", Map.of("type", List.of("string", "null"), "description", "Filing status selected: Single, Married filing jointly, Married filing separately, Head of household, or Qualifying widow(er)"));
properties.put("age_blindness_status", Map.of("type", List.of("string", "null"), "description", "Age/blindness status for standard deduction calculation (born before Jan 2, 1957 or blind)"));
properties.put("primary_taxpayer_ssn", Map.of("type", List.of("string", "null"), "description", "Social security number of primary taxpayer"));
properties.put("primary_taxpayer_name", Map.of("type", List.of("string", "null"), "description", "First name, middle initial, and last name of primary taxpayer"));
properties.put("virtual_currency_transaction", Map.of("type", List.of("string", "null"), "description", "Whether taxpayer received, sold, exchanged, or disposed of virtual currency during tax year (Yes/No)"));
properties.put("presidential_election_campaign_contribution", Map.of("type", List.of("boolean", "null"), "description", "Whether taxpayer or spouse elected to contribute $3 to Presidential Election Campaign fund"));
schema.put("properties", properties);
return schema;
}
public static void main(String[] args) {
try {
Map<String, Object> workflow = buildWorkflow();
String workflowName = (String) workflow.get("name");
System.out.println("Deploying \"" + workflowName + "\"…");
if (state.workflowId != null && !state.workflowId.isEmpty()) {
System.out.println("✓ workflow already provisioned (" + state.workflowId + ") — updating steps");
Map<String, Object> updateBody = new LinkedHashMap<>();
updateBody.put("steps", workflow.get("steps"));
api("POST", "/workflows/" + state.workflowId, updateBody);
} else {
try {
String encoded = URLEncoder.encode(workflowName, StandardCharsets.UTF_8);
Map<String, Object> list = api("GET", "/workflows?name=" + encoded, null);
List<?> items = (List<?>) list.getOrDefault("data", list.getOrDefault("items", List.of()));
for (Object item : items) {
if (item instanceof Map) {
Map<?, ?> itemMap = (Map<?, ?>) item;
if (workflowName.equals(itemMap.get("name"))) {
Object id = itemMap.get("id");
if (id != null) {
state.workflowId = id.toString();
saveState();
System.out.println("✓ workflow \"" + workflowName + "\" found in your account (" + state.workflowId + ") — updating steps");
Map<String, Object> updateBody = new LinkedHashMap<>();
updateBody.put("steps", workflow.get("steps"));
api("POST", "/workflows/" + state.workflowId, updateBody);
break;
}
}
}
}
} catch (Exception e) {
// Lookup is best-effort; fall through to create
}
if (state.workflowId == null || state.workflowId.isEmpty()) {
Map<String, Object> created = api("POST", "/workflows", workflow);
String wfId = (String) created.getOrDefault("id", null);
if (wfId == null) {
Map<?, ?> workflowObj = (Map<?, ?>) created.get("workflow");
if (workflowObj != null) wfId = (String) workflowObj.get("id");
}
if (wfId == null) throw new RuntimeException("Could not read created workflow id from response");
state.workflowId = wfId;
saveState();
System.out.println("+ created workflow (" + wfId + ")");
}
}
try {
api("POST", "/workflows/" + state.workflowId + "/versions", new LinkedHashMap<>());
} catch (Exception e) {
// Best-effort; some accounts/plans may not require this
}
System.out.println("\nDone. Run documents through it with:");
System.out.println(" POST " + API + "/workflow_runs { workflow: { id: \"" + state.workflowId + "\" }, file: { url: \"https://…\" } }");
System.out.println("Or open the workflow in the Extend dashboard to review and deploy it.");
} catch (Exception e) {
System.err.println(e.getMessage() != null ? e.getMessage() : e);
System.exit(1);
}
}
}// This code uses the Extend REST API directly because Extend has no official Go SDK yet.
// It deploys the "U.S. Individual Income Tax Return" pipeline to your Extend account.
//
// Usage:
// export EXTEND_API_KEY=sk_... (from https://dashboard.extend.ai → API Keys)
// go run provision.go
//
// Generated by doc1 (template: 1040-extractor).
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
)
const (
API = "https://api.extend.ai"
VERSION = "2026-02-09"
)
var (
apiKey string
stateDir string
stateFile string
)
type State struct {
WorkflowID string `json:"workflowId,omitempty"`
}
var state State
func init() {
apiKey = os.Getenv("EXTEND_API_KEY")
if apiKey == "" {
fmt.Fprintf(os.Stderr, "Set EXTEND_API_KEY first.\n")
os.Exit(1)
}
cwd, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to get working directory: %v\n", err)
os.Exit(1)
}
stateDir = filepath.Join(cwd, ".extend")
stateFile = filepath.Join(stateDir, "1040-extractor.json")
// Load existing state if it exists
if data, err := os.ReadFile(stateFile); err == nil {
json.Unmarshal(data, &state)
}
}
func saveState() error {
if err := os.MkdirAll(stateDir, 0755); err != nil {
return err
}
data, err := json.MarshalIndent(state, "", " ")
if err != nil {
return err
}
return os.WriteFile(stateFile, data, 0644)
}
func apiCall(method, pathName string, body interface{}) (map[string]interface{}, error) {
var reqBody io.Reader
if body != nil {
data, err := json.Marshal(body)
if err != nil {
return nil, err
}
reqBody = bytes.NewReader(data)
}
req, err := http.NewRequest(method, API+pathName, reqBody)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
req.Header.Set("x-extend-api-version", VERSION)
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var data map[string]interface{}
json.Unmarshal(respBody, &data)
if resp.StatusCode >= 400 {
respStr := string(respBody)
if len(respStr) > 300 {
respStr = respStr[:300]
}
return nil, fmt.Errorf("%s %s failed (%d): %s", method, pathName, resp.StatusCode, respStr)
}
return data, nil
}
var workflow = map[string]interface{}{
"name": "U.S. Individual Income Tax Return Processing Pipeline",
"steps": []map[string]interface{}{
{
"name": "startTrigger1",
"type": "TRIGGER",
"next": []map[string]interface{}{
{
"step": "parse1",
},
},
},
{
"name": "parse1",
"type": "PARSE",
"config": map[string]interface{}{
"parseConfig": map[string]interface{}{
"blockOptions": map[string]interface{}{
"text": map[string]interface{}{
"agentic": map[string]interface{}{
"enabled": true,
},
},
},
"chunkingStrategy": map[string]interface{}{
"type": "document",
},
},
},
"next": []map[string]interface{}{
{
"step": "extraction2",
},
},
},
{
"name": "extraction2",
"type": "EXTRACT",
"config": map[string]interface{}{
"extractorConfig": map[string]interface{}{
"schema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"tax_year": map[string]interface{}{
"type": []string{"string", "null"},
"description": "The tax year of the return (e.g., 2021)",
},
"dependents": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"ssn": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Social security number of dependent",
},
"name": map[string]interface{}{
"type": []string{"string", "null"},
"description": "First and last name of dependent",
},
"relationship": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Relationship to taxpayer (e.g., Daughter, Son, Parent)",
},
"qualifies_for_child_tax_credit": map[string]interface{}{
"type": []string{"boolean", "null"},
"description": "Whether dependent qualifies for child tax credit",
},
},
},
"description": "List of dependents claimed on return",
},
"spouse_ssn": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Social security number of spouse (if joint return)",
},
"spouse_name": map[string]interface{}{
"type": []string{"string", "null"},
"description": "First name, middle initial, and last name of spouse (if joint return)",
},
"home_address": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Full home address including street, city, state, and ZIP code",
},
"filing_status": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Filing status selected: Single, Married filing jointly, Married filing separately, Head of household, or Qualifying widow(er)",
},
"age_blindness_status": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Age/blindness status for standard deduction calculation (born before Jan 2, 1957 or blind)",
},
"primary_taxpayer_ssn": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Social security number of primary taxpayer",
},
"primary_taxpayer_name": map[string]interface{}{
"type": []string{"string", "null"},
"description": "First name, middle initial, and last name of primary taxpayer",
},
"virtual_currency_transaction": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Whether taxpayer received, sold, exchanged, or disposed of virtual currency during tax year (Yes/No)",
},
"presidential_election_campaign_contribution": map[string]interface{}{
"type": []string{"boolean", "null"},
"description": "Whether taxpayer or spouse elected to contribute $3 to Presidential Election Campaign fund",
},
},
},
"baseProcessor": "extraction_performance",
"advancedOptions": map[string]interface{}{
"reviewAgent": map[string]interface{}{
"enabled": true,
},
"advancedMultimodalEnabled": true,
},
},
},
},
},
}
func main() {
workflowName := workflow["name"].(string)
fmt.Printf("Deploying \"%s\"…\n", workflowName)
if state.WorkflowID != "" {
fmt.Printf("✓ workflow already provisioned (%s) — updating steps\n", state.WorkflowID)
_, err := apiCall("POST", fmt.Sprintf("/workflows/%s", state.WorkflowID), map[string]interface{}{
"steps": workflow["steps"],
})
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
} else {
// Try to find an existing workflow with the same name
query := url.QueryEscape(workflowName)
list, err := apiCall("GET", fmt.Sprintf("/workflows?name=%s", query), nil)
if err == nil {
var items []map[string]interface{}
if data, ok := list["data"].([]interface{}); ok {
for _, item := range data {
if m, ok := item.(map[string]interface{}); ok {
items = append(items, m)
}
}
} else if data, ok := list["items"].([]interface{}); ok {
for _, item := range data {
if m, ok := item.(map[string]interface{}); ok {
items = append(items, m)
}
}
}
for _, item := range items {
if name, ok := item["name"].(string); ok && name == workflowName {
if id, ok := item["id"].(string); ok {
state.WorkflowID = id
saveState()
fmt.Printf("✓ workflow \"%s\" found in your account (%s) — updating steps\n", workflowName, id)
_, err := apiCall("POST", fmt.Sprintf("/workflows/%s", id), map[string]interface{}{
"steps": workflow["steps"],
})
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
break
}
}
}
}
if state.WorkflowID == "" {
created, err := apiCall("POST", "/workflows", workflow)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
var wfID string
if id, ok := created["id"].(string); ok {
wfID = id
} else if wf, ok := created["workflow"].(map[string]interface{}); ok {
if id, ok := wf["id"].(string); ok {
wfID = id
}
}
if wfID == "" {
fmt.Fprintf(os.Stderr, "Could not read created workflow id from response\n")
os.Exit(1)
}
state.WorkflowID = wfID
saveState()
fmt.Printf("+ created workflow (%s)\n", wfID)
}
}
// Deploy the current draft as a new version (best-effort)
apiCall("POST", fmt.Sprintf("/workflows/%s/versions", state.WorkflowID), map[string]interface{}{})
fmt.Println("\nDone. Run documents through it with:")
fmt.Printf(" POST %s/workflow_runs { workflow: { id: \"%s\" }, file: { url: \"https://…\" } }\n", API, state.WorkflowID)
fmt.Println("Or open the workflow in the Extend dashboard to review and deploy it.")
}The U.S. Individual Income Tax Return (Form 1040) is the primary document filed annually by individuals to report income and calculate tax liability. This template captures filing status, personal identification, dependent information, and key tax questions. It requires handling of checkboxes, tables for dependents, and multi-line address fields typical of government tax forms.