{"openapi":"3.1.0","info":{"title":"POST /api/v1/transactions/bulk-upload/from-statement","version":"1.0.0","description":"Extract transactions from a statement"},"servers":[{"url":"https://api.ondayzero.com","description":"Production"}],"paths":{"/api/v1/transactions/bulk-upload/from-statement":{"post":{"tags":["transactions"],"summary":"Extract transactions from a statement","description":"Read a statement (PDF, image, CSV or Excel) with the statement parser and write\nits transactions as a CSV in the shape the bulk uploader accepts\n(`date,amount,description`). Nothing is imported.\n\n- Credit-card ledgers get charge-positive amounts, bank ledgers deposit-positive,\n  exactly as `/bulk-upload` expects.\n- Pending rows are left out unless `include_pending` is true.\n- When the statement shows opening and closing balances, the response reports\n  whether the rows tie to them (`balance_check.delta_cents`).\n\nPass the returned `s3_key` and the same `ledger_id` to `/bulk-upload/preview`\nand `/bulk-upload`.","operationId":"statement_ingest_extract_from_statement","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/StatementExtractRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessEnvelope_StatementExtractResponse_"}}}},"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_extract_from_statement_api_v1_transactions_bulk_upload_from_statement_post"}}},"components":{"schemas":{"StatementBalanceCheck":{"properties":{"checked":{"type":"boolean","title":"Checked","description":"False when the statement gave no balances."},"balanced":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Balanced"},"opening_balance_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Opening Balance Cents"},"closing_balance_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Closing Balance Cents"},"rows_total_cents":{"type":"integer","title":"Rows Total Cents","description":"Sum of the posted rows in cash-flow sign (money in positive)."},"expected_total_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Expected Total Cents"},"delta_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Delta Cents","description":"rows_total_cents minus expected_total_cents; 0 = ties."}},"type":"object","required":["checked","rows_total_cents"],"title":"StatementBalanceCheck","description":"Do the extracted rows tie to the statement's opening → closing balance?"},"StatementExtractRequest":{"properties":{"s3_key":{"type":"string","minLength":1,"title":"S3 Key","description":"S3 key of the uploaded statement (PDF, PNG/JPG, CSV or Excel)."},"ledger_id":{"type":"string","title":"Ledger Id","description":"Ledger the rows will be uploaded to. Its type decides the sign convention: credit-card ledgers get charge-positive amounts."},"include_pending":{"type":"boolean","title":"Include Pending","description":"Also write rows the statement marks pending (default: posted only).","default":false}},"type":"object","required":["s3_key","ledger_id"],"title":"StatementExtractRequest","description":"Request to turn a statement file into a bulk-upload CSV."},"StatementExtractResponse":{"properties":{"s3_key":{"type":"string","title":"S3 Key","description":"CSV to pass to /bulk-upload/preview and /bulk-upload."},"ledger_id":{"type":"string","title":"Ledger Id"},"ledger_name":{"type":"string","title":"Ledger Name"},"is_credit_card":{"type":"boolean","title":"Is Credit Card"},"sign_convention":{"type":"string","title":"Sign Convention"},"summary":{"$ref":"#/components/schemas/StatementSummary"},"balance_check":{"$ref":"#/components/schemas/StatementBalanceCheck"},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings"},"preview":{"items":{"$ref":"#/components/schemas/StatementPreviewRow"},"type":"array","title":"Preview"},"next_step":{"type":"string","title":"Next Step"}},"type":"object","required":["s3_key","ledger_id","ledger_name","is_credit_card","sign_convention","summary","balance_check","next_step"],"title":"StatementExtractResponse","description":"Result of a statement extraction; ``s3_key`` feeds the bulk-upload path."},"StatementPreviewRow":{"properties":{"date":{"type":"string","title":"Date"},"amount_cents":{"type":"integer","title":"Amount Cents","description":"Amount in the CSV's sign convention."},"description":{"type":"string","title":"Description"},"status":{"type":"string","enum":["posted","pending"],"title":"Status"}},"type":"object","required":["date","amount_cents","description","status"],"title":"StatementPreviewRow","description":"One extracted row as it appears in the generated CSV."},"StatementSummary":{"properties":{"row_count":{"type":"integer","title":"Row Count"},"posted_count":{"type":"integer","title":"Posted Count"},"pending_included":{"type":"integer","title":"Pending Included"},"pending_excluded":{"type":"integer","title":"Pending Excluded"},"skipped_rows":{"type":"integer","title":"Skipped Rows"},"money_in_cents":{"type":"integer","title":"Money In Cents"},"money_out_cents":{"type":"integer","title":"Money Out Cents"},"statement_start":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Statement Start"},"statement_end":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Statement End"},"account_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Account Name"},"bank_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bank Name"},"opening_balance_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Opening Balance Cents"},"closing_balance_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Closing Balance Cents"}},"type":"object","required":["row_count","posted_count","pending_included","pending_excluded","skipped_rows","money_in_cents","money_out_cents"],"title":"StatementSummary","description":"Counts, totals and statement metadata for the extraction."},"SuccessEnvelope_StatementExtractResponse_":{"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/StatementExtractResponse"},{"type":"null"}]}},"additionalProperties":true,"type":"object","title":"SuccessEnvelope[StatementExtractResponse]"},"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":[]}]}