{"openapi":"3.1.0","info":{"title":"POST /api/v1/credit/bank-feeds/{bank_feed_id}/ingest","version":"1.0.0","description":"Push bank accounts, transactions, and balances into a bank feed"},"servers":[{"url":"https://api.ondayzero.com","description":"Production"}],"paths":{"/api/v1/credit/bank-feeds/{bank_feed_id}/ingest":{"post":{"tags":["credit"],"summary":"Push bank accounts, transactions, and balances into a bank feed","description":"For partners that pull merchant bank data in their own backend and push it directly into DayZero without BigQuery (`provider: direct_push`, or to seed a feed immediately). Each cash account gets a bank ledger on the merchant's DayZero business, posted transactions are booked with journal entries, voided transactions are hidden, and daily balance snapshots are interpolated across the history so underwriting and bank-settlement payment matching read them immediately. Idempotent on `transaction_id` and `(account_id, as_of)`.","operationId":"ingest_credit_bank_feed","parameters":[{"name":"bank_feed_id","in":"path","required":true,"schema":{"type":"string","description":"The bank feed to push bank data into.","title":"Bank Feed Id"},"description":"The bank feed to push bank data into."},{"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/BankFeedIngestRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessEnvelope_BankFeedIngestResponse_"}}}},"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":"ingest_credit_bank_feed_api_v1_credit_bank_feeds__bank_feed_id__ingest_post"}}},"components":{"schemas":{"BankFeedAccount":{"properties":{"account_id":{"type":"string","title":"Account Id","description":"The partner's account id (Stripe's fca_…)."},"institution_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Institution Name","description":"The bank."},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"The account's name."},"last4":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last4","description":"The last four digits."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"Stripe's status for the account: active, ..."},"transactions":{"type":"integer","title":"Transactions","description":"Posted transactions DayZero holds for it.","default":0},"first_transaction_on":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"First Transaction On","description":"The earliest transaction's date."},"last_transaction_on":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Last Transaction On","description":"The latest transaction's date."},"balance_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Balance Cents","description":"The latest reported current balance, in cents."},"balance_on":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Balance On","description":"The day that balance is for."},"skipped_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Skipped Reason","description":"Why the account's data is not used: covered_by_plaid (the same account already reaches DayZero through Plaid, so taking it twice would count its money twice) or credit_account (a card, not cash)."}},"type":"object","required":["account_id"],"title":"BankFeedAccount","description":"One account in the feed. No numbers."},"BankFeedIngestAccount":{"properties":{"account_id":{"type":"string","maxLength":128,"title":"Account Id","description":"Your stable id for the bank account (e.g. `fca_...` or `acct_...`)."},"institution_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Institution Name","description":"The bank's name."},"display_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Display Name","description":"The account's display name."},"last4":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"title":"Last4","description":"The last four digits of the account."},"status":{"type":"string","maxLength":32,"title":"Status","description":"Account status (`active`, `inactive`).","default":"active"},"type":{"type":"string","enum":["cash","credit"],"title":"Type","description":"Account kind (`cash` is synced to a bank ledger; `credit` is skipped).","default":"cash"}},"additionalProperties":false,"type":"object","required":["account_id"],"title":"BankFeedIngestAccount","description":"One merchant bank account pushed directly by the partner."},"BankFeedIngestBalance":{"properties":{"account_id":{"type":"string","maxLength":128,"title":"Account Id","description":"The `account_id` this balance snapshot belongs to."},"as_of":{"type":"string","format":"date","title":"As Of","description":"The calendar day this closing balance applies to."},"current_balance_cents":{"type":"integer","title":"Current Balance Cents","description":"Closing balance on `as_of`, in cents."},"currency":{"type":"string","maxLength":3,"minLength":3,"title":"Currency","description":"Three-letter ISO currency code of the balance. Underwriting reads balances in the merchant business's own currency only; balances in any other currency are stored and left out of the measures, never converted.","default":"USD"},"type":{"type":"string","enum":["cash","credit"],"title":"Type","description":"Account kind (`cash` or `credit`).","default":"cash"}},"additionalProperties":false,"type":"object","required":["account_id","as_of","current_balance_cents"],"title":"BankFeedIngestBalance","description":"One daily closing balance snapshot pushed directly by the partner."},"BankFeedIngestRequest":{"properties":{"accounts":{"items":{"$ref":"#/components/schemas/BankFeedIngestAccount"},"type":"array","maxItems":100,"title":"Accounts","description":"Bank accounts on the merchant's feed."},"transactions":{"items":{"$ref":"#/components/schemas/BankFeedIngestTransaction"},"type":"array","maxItems":5000,"title":"Transactions","description":"Bank transactions to upsert or void."},"balances":{"items":{"$ref":"#/components/schemas/BankFeedIngestBalance"},"type":"array","maxItems":1000,"title":"Balances","description":"Reported closing balances; intermediate days are interpolated."}},"additionalProperties":false,"type":"object","title":"BankFeedIngestRequest","description":"Push merchant bank accounts, transactions, and balances directly into a bank feed."},"BankFeedIngestResponse":{"properties":{"bank_feed_id":{"type":"string","title":"Bank Feed Id","description":"The bank feed's id."},"merchant_business_id":{"type":"string","title":"Merchant Business Id","description":"The merchant's DayZero business where the rows landed."},"inserted":{"type":"integer","title":"Inserted","description":"Posted transactions newly created.","default":0},"updated":{"type":"integer","title":"Updated","description":"Existing transactions updated.","default":0},"voided":{"type":"integer","title":"Voided","description":"Posted transactions voided and hidden.","default":0},"pending_skipped":{"type":"integer","title":"Pending Skipped","description":"Pending transactions skipped.","default":0},"balance_days":{"type":"integer","title":"Balance Days","description":"Daily bank balance snapshot rows written or interpolated.","default":0},"accounts_used":{"type":"integer","title":"Accounts Used","description":"Cash accounts synced.","default":0},"accounts_skipped":{"type":"integer","title":"Accounts Skipped","description":"Accounts skipped (covered by Plaid or credit).","default":0},"feed":{"$ref":"#/components/schemas/BankFeedResponse","description":"The updated bank feed state."}},"type":"object","required":["bank_feed_id","merchant_business_id","feed"],"title":"BankFeedIngestResponse","description":"Result of a direct bank-feed ingest call."},"BankFeedIngestTransaction":{"properties":{"transaction_id":{"type":"string","maxLength":128,"title":"Transaction Id","description":"Your idempotency key for the bank transaction."},"account_id":{"type":"string","maxLength":128,"title":"Account Id","description":"The `account_id` this transaction belongs to."},"amount_cents":{"type":"integer","title":"Amount Cents","description":"Signed amount in cents (cash-flow sign: money into the account is positive, money leaving the account is negative)."},"currency":{"type":"string","maxLength":8,"title":"Currency","description":"Three-letter ISO currency code.","default":"USD"},"description":{"type":"string","maxLength":2000,"title":"Description","description":"The bank statement description."},"status":{"type":"string","enum":["posted","pending","void"],"title":"Status","description":"`posted` books onto the merchant's ledger; `pending` is skipped; `void` removes a previously posted transaction.","default":"posted"},"transacted_on":{"type":"string","format":"date","title":"Transacted On","description":"The calendar date the transaction posted at the bank."},"category":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Category","description":"The transaction's category as a Plaid personal-finance detailed code, e.g. `BANK_FEES_OVERDRAFT_FEES`, `BANK_FEES_INSUFFICIENT_FUNDS`, `INCOME_OTHER_INCOME`. Read by the same measures that read Plaid's (`nsf_count_12m` counts the two bank-fee codes); other codes are kept as a categorisation hint. Optional: without it the description is read."},"category_source":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Category Source","description":"Where `category` came from, e.g. `plaid`, `stripe_fc`, `partner`. Recorded with the transaction; optional."}},"additionalProperties":false,"type":"object","required":["transaction_id","account_id","amount_cents","description","transacted_on"],"title":"BankFeedIngestTransaction","description":"One bank transaction pushed directly by the partner."},"BankFeedResponse":{"properties":{"id":{"type":"string","title":"Id","description":"The bank feed's id."},"status":{"type":"string","enum":["pending","active","error","removed"],"title":"Status","description":"pending: registered, first sync not finished. active: synced and kept current. error: the last sync failed (`last_error`); it is tried again. removed: no longer synced."},"created":{"type":"boolean","title":"Created","description":"False when the call returned the feed already registered.","default":false},"provider":{"type":"string","title":"Provider","description":"stripe_fc_bigquery."},"app_id":{"type":"string","title":"App Id","description":"Your organisation id the dataset is named after."},"dataset":{"type":"string","title":"Dataset","description":"The BigQuery dataset read, project.dataset."},"program_id":{"type":"string","title":"Program Id","description":"The program the feed is under."},"credit_line_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Credit Line Id","description":"The line, if any."},"external_merchant_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Merchant Id","description":"Your id for the merchant."},"merchant_business_id":{"type":"string","title":"Merchant Business Id","description":"The merchant's DayZero business the data lands on: the `subject_business_id` for `POST /credit/underwriting/assessments`."},"merchant_business_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Merchant Business Name","description":"The merchant business's name."},"merchant_business_created":{"type":"boolean","title":"Merchant Business Created","description":"Whether registering this feed created the merchant's business.","default":false},"first_synced_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"First Synced At","description":"When the history first landed, UTC. Underwriting waits for it."},"last_synced_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Synced At","description":"The last successful sync, UTC."},"last_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Error","description":"While status is error: not_shared (the dataset is not readable by DayZero's service account: grant it READER), not_found (no dataset for this app_id), not_configured, auth_failed, bigquery_error or sync_failed."},"last_error_detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Error Detail","description":"What the failing call said."},"latest_transaction_on":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Latest Transaction On","description":"The newest transaction's date across accounts."},"latest_balance_on":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Latest Balance On","description":"The newest reported balance's date."},"accounts":{"items":{"$ref":"#/components/schemas/BankFeedAccount"},"type":"array","title":"Accounts","description":"The accounts seen in the dataset."},"sandbox":{"type":"boolean","title":"Sandbox","description":"True on a sandbox program.","default":false},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At","description":"When the feed was registered, UTC."}},"type":"object","required":["id","status","provider","app_id","dataset","program_id","merchant_business_id"],"title":"BankFeedResponse","description":"A merchant's bank feed and where it stands."},"SuccessEnvelope_BankFeedIngestResponse_":{"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/BankFeedIngestResponse"},{"type":"null"}]}},"additionalProperties":true,"type":"object","title":"SuccessEnvelope[BankFeedIngestResponse]"},"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":[]}]}