{"openapi":"3.1.0","info":{"title":"POST /api/v1/transactions/enrich-from-export","version":"1.0.0","description":"Enrich transactions from a detailed bank export"},"servers":[{"url":"https://api.ondayzero.com","description":"Production"}],"paths":{"/api/v1/transactions/enrich-from-export":{"post":{"tags":["transactions"],"summary":"Enrich transactions from a detailed bank export","description":"Match the rows of a bank \"account activity\" export to transactions already in a\nledger (same amount, date within `date_tolerance_days`, count-aware, exact\ndescription preferred) and add what the export knows: wire originators,\nmobile-deposit payers and other detail columns are appended to the description\nand set as the counterparty.\n\nColumns are auto-detected (originator, payer, beneficiary, additional details,\nmemo, ...) and echoed back as `column_mapping`; override them with\n`column_mapping`. `dry_run` (default true) returns the proposals without\nwriting. A description or counterparty a person set by hand is left alone unless\n`overwrite_user_edits` is true; a counterparty derived from invoices/bills is\nnever replaced.","operationId":"statement_ingest_enrich_from_export","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/EnrichFromExportRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessEnvelope_EnrichFromExportResponse_"}}}},"400":{"description":"Bad Request - Invalid input","content":{"application/json":{"example":{"error":"validation_error","message":"Validation failed: due_date: Input should be a valid date","errors":{"due_date":"Input should be a valid date"},"code":"GEN_002","request_id":"3f0e7c2a-9b4d-4e1a-8c6f-2d5b7a1e9c30"},"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or expired token, an MCP OAuth token used against the REST API, or no access to this business","content":{"application/json":{"example":{"error":"unauthorized","message":"Authentication required","code":"AUTH_001","request_id":"3f0e7c2a-9b4d-4e1a-8c6f-2d5b7a1e9c30"},"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"example":{"error":"forbidden","message":"You don't have permission to access this resource","code":"AUTH_002","request_id":"3f0e7c2a-9b4d-4e1a-8c6f-2d5b7a1e9c30"},"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not Found - Resource does not exist","content":{"application/json":{"example":{"error":"not_found","message":"Resource not found","code":"NOT_FOUND_001","request_id":"3f0e7c2a-9b4d-4e1a-8c6f-2d5b7a1e9c30"},"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"validation_error","message":"Validation failed: amount: Input should be a valid integer","errors":{"amount":"Input should be a valid integer"},"code":"GEN_002","request_id":"3f0e7c2a-9b4d-4e1a-8c6f-2d5b7a1e9c30"}}}}},"x-fastapi-operation-id":"statement_ingest_enrich_from_export_api_v1_transactions_enrich_from_export_post"}}},"components":{"schemas":{"EnrichApplied":{"properties":{"descriptions_updated":{"type":"integer","title":"Descriptions Updated","default":0},"counterparties_updated":{"type":"integer","title":"Counterparties Updated","default":0},"failed":{"items":{"additionalProperties":{"type":"string"},"type":"object"},"type":"array","title":"Failed"}},"type":"object","title":"EnrichApplied","description":"What an applied (non-dry-run) enrichment wrote."},"EnrichFromExportRequest":{"properties":{"s3_key":{"type":"string","minLength":1,"title":"S3 Key","description":"S3 key of the bank activity export (CSV or Excel)."},"ledger_id":{"type":"string","title":"Ledger Id","description":"Bank ledger whose existing transactions are enriched."},"dry_run":{"type":"boolean","title":"Dry Run","description":"Preview the proposed changes without writing (default true).","default":true},"overwrite_user_edits":{"type":"boolean","title":"Overwrite User Edits","description":"Also change descriptions and counterparties a person set by hand. A counterparty derived from invoices/bills is never replaced.","default":false},"column_mapping":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Column Mapping","description":"Optional {role: header} overrides: date, amount, debit, credit, description, party (name used as counterparty), detail (text appended to the description). Detected columns are echoed back."},"date_tolerance_days":{"type":"integer","maximum":7.0,"minimum":0.0,"title":"Date Tolerance Days","description":"Max days between export row and transaction.","default":2},"set_counterparty":{"type":"boolean","title":"Set Counterparty","description":"Set the counterparty to the extracted payer/originator.","default":true},"append_description":{"type":"boolean","title":"Append Description","description":"Append the extracted detail to the description.","default":true}},"type":"object","required":["s3_key","ledger_id"],"title":"EnrichFromExportRequest","description":"Request to enrich existing transactions from a detailed bank export."},"EnrichFromExportResponse":{"properties":{"dry_run":{"type":"boolean","title":"Dry Run"},"ledger_id":{"type":"string","title":"Ledger Id"},"ledger_name":{"type":"string","title":"Ledger Name"},"column_mapping":{"additionalProperties":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"},{"type":"null"}]},"type":"object","title":"Column Mapping"},"summary":{"additionalProperties":{"type":"integer"},"type":"object","title":"Summary"},"applied":{"anyOf":[{"$ref":"#/components/schemas/EnrichApplied"},{"type":"null"}]},"proposals":{"items":{"$ref":"#/components/schemas/EnrichProposal"},"type":"array","title":"Proposals"},"proposals_truncated":{"type":"integer","title":"Proposals Truncated","default":0}},"type":"object","required":["dry_run","ledger_id","ledger_name","column_mapping","summary"],"title":"EnrichFromExportResponse","description":"Preview or result of an enrichment run."},"EnrichProposal":{"properties":{"row_number":{"type":"integer","title":"Row Number"},"date":{"type":"string","title":"Date"},"amount_cents":{"type":"integer","title":"Amount Cents"},"export_description":{"type":"string","title":"Export Description","default":""},"detail":{"type":"string","title":"Detail","default":""},"status":{"type":"string","enum":["update","no_change","protected","ambiguous","unmatched","no_detail"],"title":"Status"},"transaction_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transaction Id"},"match":{"anyOf":[{"type":"string","enum":["exact_description","amount_and_date"]},{"type":"null"}],"title":"Match"},"date_diff_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Date Diff Days"},"current_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Description"},"current_counterparty":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Counterparty"},"new_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"New Description"},"new_counterparty":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"New Counterparty"},"skipped_fields":{"items":{"type":"string"},"type":"array","title":"Skipped Fields"}},"type":"object","required":["row_number","date","amount_cents","status"],"title":"EnrichProposal","description":"One export row and what (if anything) would change on its match."},"SuccessEnvelope_EnrichFromExportResponse_":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"},"code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code"},"data":{"anyOf":[{"$ref":"#/components/schemas/EnrichFromExportResponse"},{"type":"null"}]}},"additionalProperties":true,"type":"object","title":"SuccessEnvelope[EnrichFromExportResponse]"},"ErrorResponse":{"title":"ErrorResponse","type":"object","description":"Envelope returned by every non-2xx response. Branch on `code` (stable) rather than `message` (human-readable, may change).","required":["error","message","code"],"properties":{"error":{"type":"string","description":"Error category: `validation_error`, `unauthorized`, `forbidden`, `not_found`, `conflict`, `rate_limited`, `server_error`, or `service_unavailable`."},"message":{"type":"string","description":"Human-readable explanation, safe to show to end users."},"code":{"type":"string","description":"Stable machine-readable code in `CATEGORY_NNN` form (e.g. `NOT_FOUND_006`, `AUTH_010`, `GEN_002`)."},"request_id":{"type":"string","description":"Correlation id for support requests. Echoes the `x-request-id` request header when one was supplied."},"errors":{"type":"object","additionalProperties":{"type":"string"},"description":"Field-level validation messages keyed by field name. Present on 400/422 validation failures only."}}}},"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Token","description":"API token authentication. Format: `Bearer dz_...`"}}},"security":[{"BearerAuth":[]}]}