{"openapi":"3.1.0","info":{"title":"POST /api/v1/credit/programs/{program_id}/history/import/parse","version":"1.0.0","description":"Upload a payments or statements history file"},"servers":[{"url":"https://api.ondayzero.com","description":"Production"}],"paths":{"/api/v1/credit/programs/{program_id}/history/import/parse":{"post":{"tags":["credit"],"summary":"Upload a payments or statements history file","description":"Phase 2 of the migration (Appendix E): stage the previous system's payments (`kind=payments`) or statements (`kind=statements`) as CSV or XLSX, beside the loan file. The mapping is inferred (or given as `mapping`, a JSON object of field to column) and every row is staged for seven days with its problems; nothing is attached. A payments file has one row per payment, or one per loan a payment was split across, each repeating the payment's id, amount and date; a row without a loan has its split derived at the run. A statements file has one row per statement, naming its document. Open to any token on a sandbox business; on a live business an operator action (an API token receives `403`).","operationId":"credit_history_parse","parameters":[{"name":"program_id","in":"path","required":true,"schema":{"type":"string","description":"The program.","title":"Program Id"},"description":"The program."},{"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":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_credit_history_parse_api_v1_credit_programs__program_id__history_import_parse_post"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessEnvelope_HistoryParseResponse_"}}}},"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":"credit_history_parse_api_v1_credit_programs__program_id__history_import_parse_post"}}},"components":{"schemas":{"Body_credit_history_parse_api_v1_credit_programs__program_id__history_import_parse_post":{"properties":{"kind":{"type":"string","title":"Kind","description":"`payments` or `statements`."},"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File","description":"The history file: CSV or XLSX."},"mapping":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mapping","description":"JSON object of field to column, e.g. {\"paid_on\": \"Payment Date\"}."},"amounts_in":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Amounts In","description":"`dollars` or `cents`. Inferred when omitted."}},"type":"object","required":["kind","file"],"title":"Body_credit_history_parse_api_v1_credit_programs__program_id__history_import_parse_post"},"HistoryBatchTotals":{"properties":{"row_count":{"type":"integer","title":"Row Count","description":"Rows in the file.","default":0},"importable_count":{"type":"integer","title":"Importable Count","description":"Rows with no error.","default":0},"error_row_count":{"type":"integer","title":"Error Row Count","description":"Rows that will be rejected.","default":0},"warning_count":{"type":"integer","title":"Warning Count","description":"Warnings across every row.","default":0},"unassigned_row_count":{"type":"integer","title":"Unassigned Row Count","description":"Rows with no line key, so on no line.","default":0},"line_count":{"type":"integer","title":"Line Count","description":"Distinct line keys in the file.","default":0},"unmatched_line_count":{"type":"integer","title":"Unmatched Line Count","description":"Line keys with no line on the program. History opens no line.","default":0},"record_count":{"type":"integer","title":"Record Count","description":"Payments (rows grouped by payment id; a split takes one row per loan) or statements in the file.","default":0},"amount_cents":{"type":"integer","title":"Amount Cents","description":"Payments: their amounts. Statements: their statement balances. Over importable rows.","default":0}},"type":"object","title":"HistoryBatchTotals","description":"What a staged history file holds under its mapping."},"HistoryDocument":{"properties":{"filename":{"type":"string","title":"Filename","description":"The name the statements file's `document` column refers to it by."},"sha256":{"type":"string","title":"Sha256","description":"SHA-256 of the document."},"size_bytes":{"type":"integer","title":"Size Bytes","description":"Its size in bytes."},"content_type":{"type":"string","title":"Content Type","description":"Its media type: `application/pdf`."}},"type":"object","required":["filename","sha256","size_bytes","content_type"],"title":"HistoryDocument","description":"A statement document added to a statements batch."},"HistoryParseResponse":{"properties":{"staging_id":{"type":"string","title":"Staging Id","description":"The batch id. Every later step takes it."},"program_id":{"type":"string","title":"Program Id","description":"The program the file is for."},"kind":{"type":"string","enum":["payments","statements"],"title":"Kind","description":"`payments` or `statements`."},"filename":{"type":"string","title":"Filename","description":"The file's name as uploaded."},"sha256":{"type":"string","title":"Sha256","description":"SHA-256 of the file as uploaded."},"row_count":{"type":"integer","title":"Row Count","description":"Rows in the file, header excluded."},"status":{"type":"string","enum":["parsed","running","dry_run","applied","failed","expired"],"title":"Status","description":"parsed, dry_run, applied, failed (the control totals refused) or expired."},"amounts_in":{"type":"string","enum":["dollars","cents"],"title":"Amounts In","description":"The unit of every money column."},"line_key_field":{"anyOf":[{"type":"string","enum":["external_line_id","external_merchant_id"]},{"type":"null"}],"title":"Line Key Field","description":"Which key places rows on lines: `external_line_id` when mapped, else `external_merchant_id`. Null when neither is mapped."},"mapping":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":"object","title":"Mapping","description":"Target field → source column in force."},"mapping_source":{"type":"string","title":"Mapping Source","description":"`inferred` or `given`."},"amounts_in_reason":{"type":"string","title":"Amounts In Reason","description":"Why the money reads in this unit."},"totals":{"$ref":"#/components/schemas/HistoryBatchTotals","description":"What the file holds under the mapping in force."},"columns":{"items":{"$ref":"#/components/schemas/ImportColumn"},"type":"array","title":"Columns","description":"Every column of the file."},"fields":{"items":{"$ref":"#/components/schemas/ImportFieldMapping"},"type":"array","title":"Fields","description":"Every target field with its source column and confidence."},"unmatched_keys":{"items":{"type":"string"},"type":"array","title":"Unmatched Keys","description":"Line keys with no line on the program: their rows are rejected."},"problems":{"items":{"$ref":"#/components/schemas/ImportRowProblem"},"type":"array","title":"Problems","description":"Row problems, the first 200; `problem_count` has them all."},"problem_count":{"type":"integer","title":"Problem Count","description":"Every row problem in the file.","default":0},"documents":{"items":{"$ref":"#/components/schemas/HistoryDocument"},"type":"array","title":"Documents","description":"Statements batch: the documents added to it so far."},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the file was staged (UTC)."},"expires_at":{"type":"string","format":"date-time","title":"Expires At","description":"Seven days after staging. An unapplied batch is refused after it."},"applied_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Applied At","description":"When the file was attached."},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By","description":"Who staged the file."}},"type":"object","required":["staging_id","program_id","kind","filename","sha256","row_count","status","amounts_in","mapping_source","amounts_in_reason","created_at","expires_at"],"title":"HistoryParseResponse","description":"A staged payments or statements file, with its mapping to confirm."},"ImportColumn":{"properties":{"name":{"type":"string","title":"Name","description":"The header as it appears in the file."},"samples":{"items":{"type":"string"},"type":"array","title":"Samples","description":"Up to three non-empty values from the top of the file."}},"type":"object","required":["name"],"title":"ImportColumn","description":"One column of the uploaded file, with a taste of what it holds."},"ImportFieldMapping":{"properties":{"target":{"type":"string","title":"Target","description":"The loan import field (or line key) to fill."},"kind":{"type":"string","title":"Kind","description":"id, text, date, money, integer, rate, boolean or last_four."},"required":{"type":"boolean","title":"Required","description":"A row cannot load without this field."},"description":{"type":"string","title":"Description","description":"What the field means.","default":""},"source_column":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Column","description":"The file column it is read from; null for none."},"confidence":{"type":"number","title":"Confidence","description":"0 to 1. 1 is a column set by hand; below 0.5 deserves a look."},"reason":{"type":"string","title":"Reason","description":"Why this column, in words."},"samples":{"items":{"type":"string"},"type":"array","title":"Samples","description":"Up to three values of the source column, as they arrived."}},"type":"object","required":["target","kind","required","confidence","reason"],"title":"ImportFieldMapping","description":"Where one target field is read from, and how sure the guess is."},"ImportRowProblem":{"properties":{"row_number":{"type":"integer","title":"Row Number","description":"Position in the file, from 1, header excluded."},"field":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Field","description":"The target field at fault, when there is one."},"message":{"type":"string","title":"Message","description":"What is wrong, in words."},"severity":{"type":"string","enum":["error","warning"],"title":"Severity","description":"`error`: the row will be rejected. `warning`: it loads as described."}},"type":"object","required":["row_number","message","severity"],"title":"ImportRowProblem","description":"Something wrong with one row of the file."},"SuccessEnvelope_HistoryParseResponse_":{"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/HistoryParseResponse"},{"type":"null"}]}},"additionalProperties":true,"type":"object","title":"SuccessEnvelope[HistoryParseResponse]"},"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":[]}]}