Skills extract-invoices-to-spreadsheet
Extract structured data from multiple invoice PDFs in one call, then pipe the results directly into an XLSX accounts payable tracker.
install
source · Clone the upstream repo
git clone https://github.com/iterationlayer/skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/iterationlayer/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/extract-invoices-to-spreadsheet" ~/.claude/skills/iterationlayer-skills-extract-invoices-to-spreadsheet && rm -rf "$T"
manifest:
skills/extract-invoices-to-spreadsheet/SKILL.mdsource content
Extract Invoices and Build Accounts Payable Spreadsheet
Accounts payable teams receive PDF invoices from multiple vendors and need to log them into a spreadsheet for the approval workflow. This recipe extracts structured data from all invoices in a single call, then feeds the results directly into an XLSX tracker with one row per invoice.
APIs Used
Document Extraction (1 credit per page), Sheet Generation (2 credits/request)
Prerequisites
You need an Iteration Layer API key. Get one at platform.iterationlayer.com — free trial credits included, no credit card required.
For full integration guidance (SDKs, auth, MCP, error handling), see the Iteration Layer Integration Guide.
Implementation
# Step 1: Extract structured data from all three invoices EXTRACTION=$(curl -s -X POST https://api.iterationlayer.com/document-extraction/v1/extract \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "files": [ { "type": "url", "name": "invoice-acme.pdf", "url": "https://example.com/invoices/invoice-acme.pdf" }, { "type": "url", "name": "invoice-brightway.pdf", "url": "https://example.com/invoices/invoice-brightway.pdf" }, { "type": "url", "name": "invoice-delta-freight.pdf", "url": "https://example.com/invoices/invoice-delta-freight.pdf" } ], "schema": { "fields": [ { "name": "invoices", "type": "ARRAY", "description": "One entry per invoice file", "fields": [ { "name": "vendor_name", "type": "TEXT", "description": "Vendor or supplier name" }, { "name": "invoice_number", "type": "TEXT", "description": "Invoice reference number" }, { "name": "invoice_date", "type": "DATE", "description": "Invoice issue date" }, { "name": "due_date", "type": "DATE", "description": "Payment due date" }, { "name": "total_amount", "type": "CURRENCY_AMOUNT", "description": "Total invoice amount" }, { "name": "currency", "type": "CURRENCY_CODE", "description": "Invoice currency" } ] } ] } }') # Step 2: Generate XLSX accounts payable tracker from the extracted values curl -X POST https://api.iterationlayer.com/sheet-generation/v1/generate \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "format": "xlsx", "styles": { "header": { "is_bold": true, "background_color": "#1E3A5F", "font_color": "#FFFFFF" }, "body": { "font_color": "#333333" } }, "sheets": [ { "name": "Accounts Payable", "columns": [ { "name": "Vendor", "width": 30 }, { "name": "Invoice #", "width": 18 }, { "name": "Invoice Date", "width": 14 }, { "name": "Due Date", "width": 14 }, { "name": "Amount", "width": 16 }, { "name": "Currency", "width": 12 }, { "name": "Status", "width": 14 } ], "rows": [ [ { "value": "Acme Office Supplies" }, { "value": "INV-2026-0041" }, { "value": "2026-03-01", "format": "date" }, { "value": "2026-03-31", "format": "date" }, { "value": 888.98, "format": "currency" }, { "value": "USD" }, { "value": "Pending" } ], [ { "value": "Brightway Marketing" }, { "value": "BWM-10094" }, { "value": "2026-03-05", "format": "date" }, { "value": "2026-04-04", "format": "date" }, { "value": 3450.00, "format": "currency" }, { "value": "USD" }, { "value": "Pending" } ], [ { "value": "Delta Freight Solutions" }, { "value": "DFS-88321" }, { "value": "2026-03-10", "format": "date" }, { "value": "2026-04-09", "format": "date" }, { "value": 554.85, "format": "currency" }, { "value": "USD" }, { "value": "Pending" } ] ] } ] }'
import { IterationLayer } from "iterationlayer"; const client = new IterationLayer({ apiKey: "YOUR_API_KEY" }); // Step 1: Extract structured data from all three invoices const extraction = await client.extract({ files: [ { type: "url", name: "invoice-acme.pdf", url: "https://example.com/invoices/invoice-acme.pdf", }, { type: "url", name: "invoice-brightway.pdf", url: "https://example.com/invoices/invoice-brightway.pdf", }, { type: "url", name: "invoice-delta-freight.pdf", url: "https://example.com/invoices/invoice-delta-freight.pdf", }, ], schema: { fields: [ { name: "invoices", type: "ARRAY", description: "One entry per invoice file", fields: [ { name: "vendor_name", type: "TEXT", description: "Vendor or supplier name", }, { name: "invoice_number", type: "TEXT", description: "Invoice reference number", }, { name: "invoice_date", type: "DATE", description: "Invoice issue date", }, { name: "due_date", type: "DATE", description: "Payment due date" }, { name: "total_amount", type: "CURRENCY_AMOUNT", description: "Total invoice amount", }, { name: "currency", type: "CURRENCY_CODE", description: "Invoice currency", }, ], }, ], }, }); const invoices = extraction.invoices.value as Array< Record<string, { value: unknown }> >; // Step 2: Generate XLSX accounts payable tracker from the extracted values const sheet = await client.generateSheet({ format: "xlsx", styles: { header: { is_bold: true, background_color: "#1E3A5F", font_color: "#FFFFFF", }, body: { font_color: "#333333", }, }, sheets: [ { name: "Accounts Payable", columns: [ { name: "Vendor", width: 30 }, { name: "Invoice #", width: 18 }, { name: "Invoice Date", width: 14 }, { name: "Due Date", width: 14 }, { name: "Amount", width: 16 }, { name: "Currency", width: 12 }, { name: "Status", width: 14 }, ], rows: invoices.map((invoice) => [ { value: String(invoice.vendor_name.value) }, { value: String(invoice.invoice_number.value) }, { value: String(invoice.invoice_date.value), format: "date" }, { value: String(invoice.due_date.value), format: "date" }, { value: Number( (invoice.total_amount.value as { amount: string }).amount, ), format: "currency", }, { value: String(invoice.currency.value) }, { value: "Pending" }, ]), }, ], });
from iterationlayer import IterationLayer client = IterationLayer(api_key="YOUR_API_KEY") # Step 1: Extract structured data from all three invoices extraction = client.extract( files=[ { "type": "url", "name": "invoice-acme.pdf", "url": "https://example.com/invoices/invoice-acme.pdf", }, { "type": "url", "name": "invoice-brightway.pdf", "url": "https://example.com/invoices/invoice-brightway.pdf", }, { "type": "url", "name": "invoice-delta-freight.pdf", "url": "https://example.com/invoices/invoice-delta-freight.pdf", }, ], schema={ "fields": [ { "name": "invoices", "type": "ARRAY", "description": "One entry per invoice file", "fields": [ {"name": "vendor_name", "type": "TEXT", "description": "Vendor or supplier name"}, {"name": "invoice_number", "type": "TEXT", "description": "Invoice reference number"}, {"name": "invoice_date", "type": "DATE", "description": "Invoice issue date"}, {"name": "due_date", "type": "DATE", "description": "Payment due date"}, {"name": "total_amount", "type": "CURRENCY_AMOUNT", "description": "Total invoice amount"}, {"name": "currency", "type": "CURRENCY_CODE", "description": "Invoice currency"}, ], } ] }, ) invoices = extraction["invoices"]["value"] # Step 2: Generate XLSX accounts payable tracker from the extracted values sheet = client.generate_sheet( format="xlsx", styles={ "header": { "is_bold": True, "background_color": "#1E3A5F", "font_color": "#FFFFFF", }, "body": { "font_color": "#333333", }, }, sheets=[ { "name": "Accounts Payable", "columns": [ {"name": "Vendor", "width": 30}, {"name": "Invoice #", "width": 18}, {"name": "Invoice Date", "width": 14}, {"name": "Due Date", "width": 14}, {"name": "Amount", "width": 16}, {"name": "Currency", "width": 12}, {"name": "Status", "width": 14}, ], "rows": [ [ {"value": invoice["vendor_name"]["value"]}, {"value": invoice["invoice_number"]["value"]}, {"value": invoice["invoice_date"]["value"], "format": "date"}, {"value": invoice["due_date"]["value"], "format": "date"}, {"value": float(invoice["total_amount"]["value"]["amount"]), "format": "currency"}, {"value": invoice["currency"]["value"]}, {"value": "Pending"}, ] for invoice in invoices ], } ], )
package main import ( "fmt" il "github.com/iterationlayer/sdk-go" ) func main() { client := il.NewClient("YOUR_API_KEY") // Step 1: Extract structured data from all three invoices extraction, err := client.Extract(il.ExtractRequest{ Files: []il.FileInput{ il.NewFileFromURL("invoice-acme.pdf", "https://example.com/invoices/invoice-acme.pdf"), il.NewFileFromURL("invoice-brightway.pdf", "https://example.com/invoices/invoice-brightway.pdf"), il.NewFileFromURL("invoice-delta-freight.pdf", "https://example.com/invoices/invoice-delta-freight.pdf"), }, Schema: il.ExtractionSchema{ "invoices": il.NewArrayFieldConfig( "invoices", "One entry per invoice file", []il.FieldConfig{ il.NewTextFieldConfig("vendor_name", "Vendor or supplier name"), il.NewTextFieldConfig("invoice_number", "Invoice reference number"), il.NewDateFieldConfig("invoice_date", "Invoice issue date"), il.NewDateFieldConfig("due_date", "Payment due date"), il.NewCurrencyAmountFieldConfig("total_amount", "Total invoice amount"), il.NewCurrencyCodeFieldConfig("currency", "Invoice currency"), }, ), }, }) if err != nil { panic(err) } invoicesRaw, _ := (*extraction)["invoices"].Value.([]interface{}) rows := make([]il.SheetRow, 0, len(invoicesRaw)) for _, item := range invoicesRaw { invoice, _ := item.(map[string]interface{}) getStr := func(field string) string { f, _ := invoice[field].(map[string]interface{}) return fmt.Sprintf("%v", f["value"]) } totalAmountField, _ := invoice["total_amount"].(map[string]interface{}) totalAmountValue, _ := totalAmountField["value"].(map[string]interface{}) amountStr := fmt.Sprintf("%v", totalAmountValue["amount"]) rows = append(rows, il.SheetRow{ {Value: getStr("vendor_name")}, {Value: getStr("invoice_number")}, {Value: getStr("invoice_date"), Format: "date"}, {Value: getStr("due_date"), Format: "date"}, {Value: amountStr, Format: "currency"}, {Value: getStr("currency")}, {Value: "Pending"}, }) } // Step 2: Generate XLSX accounts payable tracker from the extracted values _, err = client.GenerateSheet(il.GenerateSheetRequest{ Format: "xlsx", Styles: &il.SheetStyles{ Header: &il.CellStyle{ IsBold: true, BackgroundColor: "#1E3A5F", FontColor: "#FFFFFF", }, Body: &il.CellStyle{ FontColor: "#333333", }, }, Sheets: []il.Sheet{ { Name: "Accounts Payable", Columns: []il.SheetColumn{ {Name: "Vendor", Width: 30}, {Name: "Invoice #", Width: 18}, {Name: "Invoice Date", Width: 14}, {Name: "Due Date", Width: 14}, {Name: "Amount", Width: 16}, {Name: "Currency", Width: 12}, {Name: "Status", Width: 14}, }, Rows: rows, }, }, }) if err != nil { panic(err) } }
{ "name": "Extract Invoices and Build Accounts Payable Spreadsheet", "nodes": [ { "parameters": { "content": "## Extract Invoices and Build Accounts Payable Spreadsheet Accounts payable teams receive PDF invoices from multiple vendors and need to log them into a spreadsheet for the approval workflow. This recipe extracts structured data from all invoices in a single call, then feeds the results directly into an XLSX tracker with one row per invoice. **Note:** This workflow uses the Iteration Layer community node (`n8n-nodes-iterationlayer`). Install it via Settings > Community Nodes before importing. Self-hosted n8n only.", "height": 280, "width": 500, "color": 2 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 200, 40 ], "id": "b74d5200-3556-44d1-9654-3cf1b1cb17a8", "name": "Overview" }, { "parameters": { "content": "### Step 1: Extract Invoice Data Resource: **Document Extraction** Configure the Document Extraction parameters below, then connect your credentials.", "height": 160, "width": 300, "color": 6 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 475, 100 ], "id": "d2e01ad3-378d-4e57-b265-a818cc18ae11", "name": "Step 1 Note" }, { "parameters": { "content": "### Step 2: Generate AP Spreadsheet Resource: **Sheet Generation** Configure the Sheet Generation parameters below, then connect your credentials.", "height": 160, "width": 300, "color": 6 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 725, 100 ], "id": "cb8ecc73-6801-47f2-b17a-948e94a8dd19", "name": "Step 2 Note" }, { "parameters": {}, "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [ 250, 300 ], "id": "7a8b9c0d-1e2f-3456-0123-678901234567", "name": "Manual Trigger" }, { "parameters": { "resource": "documentExtraction", "schemaInputMode": "rawJson", "schemaJson": "[ { \"name\": \"invoices\", \"type\": \"ARRAY\", \"description\": \"One entry per invoice file\", \"children\": [ { \"name\": \"vendor_name\", \"type\": \"TEXT\", \"description\": \"Vendor or supplier name\" }, { \"name\": \"invoice_number\", \"type\": \"TEXT\", \"description\": \"Invoice reference number\" }, { \"name\": \"invoice_date\", \"type\": \"DATE\", \"description\": \"Invoice issue date\" }, { \"name\": \"due_date\", \"type\": \"DATE\", \"description\": \"Payment due date\" }, { \"name\": \"total_amount\", \"type\": \"CURRENCY_AMOUNT\", \"description\": \"Total invoice amount\" }, { \"name\": \"currency\", \"type\": \"CURRENCY_CODE\", \"description\": \"Invoice currency\" } ] } ]", "files": { "fileValues": [ { "inputMode": "url", "fileUrl": "https://example.com/invoices/invoice-acme.pdf" }, { "inputMode": "url", "fileUrl": "https://example.com/invoices/invoice-brightway.pdf" }, { "inputMode": "url", "fileUrl": "https://example.com/invoices/invoice-delta-freight.pdf" } ] } }, "type": "n8n-nodes-iterationlayer.iterationLayer", "typeVersion": 1, "position": [ 500, 300 ], "id": "8b9c0d1e-2f3a-4567-1234-789012345678", "name": "Extract Invoice Data", "credentials": { "iterationLayerApi": { "id": "1", "name": "Iteration Layer API" } } }, { "parameters": { "resource": "sheetGeneration", "sheetFormat": "xlsx", "sheetsJson": "[ { \"name\": \"Accounts Payable\", \"columns\": [ { \"name\": \"Vendor\", \"width\": 30 }, { \"name\": \"Invoice #\", \"width\": 18 }, { \"name\": \"Invoice Date\", \"width\": 14 }, { \"name\": \"Due Date\", \"width\": 14 }, { \"name\": \"Amount\", \"width\": 16 }, { \"name\": \"Currency\", \"width\": 12 }, { \"name\": \"Status\", \"width\": 14 } ], \"rows\": \"{{ $json.invoices }}\" } ]", "sheetStylesJson": "{ \"header\": { \"is_bold\": true, \"background_color\": \"#1E3A5F\", \"font_color\": \"#FFFFFF\" }, \"body\": { \"font_color\": \"#333333\" } }" }, "type": "n8n-nodes-iterationlayer.iterationLayer", "typeVersion": 1, "position": [ 750, 300 ], "id": "9c0d1e2f-3a4b-5678-2345-890123456789", "name": "Generate AP Spreadsheet", "credentials": { "iterationLayerApi": { "id": "1", "name": "Iteration Layer API" } } } ], "connections": { "Manual Trigger": { "main": [ [ { "node": "Extract Invoice Data", "type": "main", "index": 0 } ] ] }, "Extract Invoice Data": { "main": [ [ { "node": "Generate AP Spreadsheet", "type": "main", "index": 0 } ] ] } }, "settings": { "executionOrder": "v1" } }
Extract invoice data from the files at [file URL 1], [file URL 2], and [file URL 3], then generate an accounts payable spreadsheet. Use the extract_document tool with these fields: - invoices (ARRAY): One entry per invoice file, each with: - vendor_name (TEXT): Vendor or supplier name - invoice_number (TEXT): Invoice reference number - invoice_date (DATE): Invoice issue date - due_date (DATE): Payment due date - total_amount (CURRENCY_AMOUNT): Total invoice amount - currency (CURRENCY_CODE): Invoice currency Then use the generate_sheet tool to create an XLSX accounts payable tracker with the extracted rows.
Response
{ "success": true, "data": { "buffer": "UEsDBBQAAAAIAA...", "mime_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" } }