{"openapi":"3.1.0","info":{"title":"POST /api/v1/transactions/bulk-upload","version":"1.0.0","description":"Bulk upload transactions to new source account"},"servers":[{"url":"https://api.ondayzero.com","description":"Production"}],"paths":{"/api/v1/transactions/bulk-upload":{"post":{"tags":["transactions"],"summary":"Bulk upload transactions to new source account","description":"Upload historical transactions from a CSV/Excel file to a new source account.\n\n**Workflow (via Temporal for durability):**\n\n*Local engine:*\n1. Gets/creates ledger\n2. Reads transactions from the CSV/Excel file in S3\n3. Creates Transaction + Journal Entry rows with categorization\n4. Runs AI categorization pass for uncategorized transactions\n\n*Teal engine:*\n1. Gets/creates ledger and personal source account in Teal\n2. Reads transactions from the CSV/Excel file in S3\n3. Posts transactions to Teal source account\n4. Imports journal entries created by Teal\n\n**Supported File Formats:** .csv, .xlsx, .xls\n\n**CSV/Excel Columns (flexible naming):**\n- date/datetime/transaction_date: Transaction date (YYYY-MM-DD or ISO format)\n- amount/transaction_amount/value: Amount in dollars (positive or negative)\n- description/memo/name: Transaction description","operationId":"bulk_upload_transactions_api_v1_transactions_bulk_upload_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-business-id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Business-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkTransactionUploadRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkTransactionUploadResponse"}}}},"400":{"description":"Bad Request - Invalid input","content":{"application/json":{"example":{"detail":"Invalid request parameters"}}}},"401":{"description":"Unauthorized - Authentication required","content":{"application/json":{"example":{"detail":"Not authenticated"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"example":{"detail":"Not enough permissions"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"BulkTransactionUploadRequest":{"properties":{"ledger_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ledger Id","description":"Optional: UUID of existing ledger to use. If provided, ledger creation is skipped and ledger_name/ledger fields are ignored."},"ledger_name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Ledger Name","description":"Display name for the new ledger (required if ledger_id not provided, e.g., 'My Old Credit Card XXXX')"},"source_account_name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Source Account Name","description":"Optional: Display name for the source account. Defaults to ledger name if not provided."},"business_personal":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Business Personal","description":"Whether the source account is a business or personal account. 'business' (default) for company transactions, 'personal' for owner draws/contributions.","default":"business"},"invert_transaction_amounts":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Invert Transaction Amounts","description":"If true, transaction amounts are inverted during import. Useful when CSV has opposite sign convention.","default":false},"s3_key":{"type":"string","minLength":1,"title":"S3 Key","description":"S3 key of the CSV file containing transactions"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date","description":"Optional: Only upload transactions on or after this date (YYYY-MM-DD). Filters transactions before upload."},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date","description":"Optional: Only upload transactions on or before this date (YYYY-MM-DD). Filters transactions before upload."},"check_duplicates":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Check Duplicates","description":"If true, checks for duplicate transactions before uploading. Duplicates are skipped.","default":true},"duplicate_match_fields":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Duplicate Match Fields","description":"Fields to use for duplicate detection: 'amount', 'date', 'description'. Defaults to all three.","default":["amount","date","description"]},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency","description":"Currency for this account (USD, CAD, AUD, EUR, or GBP). Defaults to business default.","default":"USD"},"editable":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Editable","description":"If true, journal entries can be manually posted to this account.","default":true},"type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type","description":"Account type: asset, liability, equity, revenue, expense. Required in Custom Mode."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Optional description of the account's purpose."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"Account status: 'active' or 'inactive'. Inactive accounts hidden from dropdowns.","default":"active"},"debit_credit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Debit Credit","description":"Normal balance: 'debit' (assets/expenses) or 'credit' (liabilities/equity/revenue). Required in Custom Mode."},"sort_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sort Code","description":"Sort code for chart ordering (e.g., '1000' for assets, '4000' for revenue). Required in Custom Mode."},"sub_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sub Type","description":"Sub-classification (current_assets, operating_expenses, etc.). Required in Custom Mode."},"report_cash_flow":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Report Cash Flow","description":"Include in cash flow statement. Required in Custom Mode."},"financial_account_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Financial Account Type","description":"Templated Mode: bank_account, credit_card, payments, payroll, loan, prepaid_card, accounts_receivable, accounts_payable."},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id","description":"UUID of parent ledger for sub-accounts (e.g., 'Operating Expenses' → 'Office Supplies')."},"column_mapping":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Column Mapping","description":"User-supplied mapping from DayZero field names to CSV column names. Keys: 'date', 'amount', 'description'. Values: the CSV header to use. When provided, auto-detection is skipped."}},"type":"object","required":["s3_key"],"title":"BulkTransactionUploadRequest","description":"Request schema for bulk transaction upload.\n\nUpload historical transactions from a CSV file to a new source account.\nThe workflow creates a ledger (or uses existing), source account, posts transactions to Teal,\nand imports the resulting journal entries.\n\n**Two modes:**\n\n1. **Use Existing Ledger** (if ledger_id is provided):\n   - Provide `ledger_id` of an existing ledger\n   - Ledger creation is skipped\n   - `source_account_name` is optional (defaults to ledger name)\n\n2. **Create New Ledger** (if ledger_id is not provided):\n   - Provide `ledger_name` and ledger fields\n   - Supports two creation modes:\n     - **Templated Mode**: Provide `financial_account_type` (recommended)\n     - **Custom Mode**: Provide all required fields (type, debit_credit, sort_code, sub_type, report_cash_flow)\n\n**Additional Features:**\n- Date range filtering: Use `start_date` and `end_date` to filter transactions before upload\n- Duplicate detection: Use `check_duplicates` (default: true) to automatically skip duplicate transactions\n- Custom duplicate matching: Use `duplicate_match_fields` to customize which fields are used for duplicate detection","examples":[{"financial_account_type":"credit_card","ledger_name":"My Old Credit Card XXXX","s3_key":"uploads/business123/transactions.csv"},{"ledger_id":"019abc12-3456-7890-abcd-ef1234567890","s3_key":"uploads/business123/transactions.csv","source_account_name":"Historical Import Source"},{"debit_credit":"debit","editable":true,"ledger_name":"Historical Bank Account","report_cash_flow":true,"s3_key":"uploads/business123/transactions.csv","sort_code":"1010","sub_type":"current_assets","type":"asset"}]},"BulkTransactionUploadResponse":{"properties":{"workflow_id":{"type":"string","title":"Workflow Id","description":"Temporal workflow ID for tracking progress"},"business_id":{"type":"string","title":"Business Id","description":"Business ID the upload is for"},"ledger_name":{"type":"string","title":"Ledger Name","description":"Name of the ledger being created"},"status":{"type":"string","title":"Status","description":"Workflow status (typically 'started' initially)"},"executor":{"type":"string","title":"Executor","description":"Execution backend (typically 'temporal')"}},"type":"object","required":["workflow_id","business_id","ledger_name","status","executor"],"title":"BulkTransactionUploadResponse","description":"Response schema for bulk transaction upload workflow.\n\nReturns workflow information since the upload runs asynchronously via Temporal.\nThe workflow is durable - if interrupted, it will resume from where it left off.","example":{"business_id":"019abc12-3456-7890-abcd-ef1234567890","executor":"temporal","ledger_name":"My Old Credit Card XXXX","status":"started","workflow_id":"bulk-upload-019abc12-1234567890"}},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Token","description":"API token authentication. Format: `Bearer dz_...`"}}},"security":[{"BearerAuth":[]}]}