> ## Documentation Index
> Fetch the complete documentation index at: https://docs.layerfi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Import bank statement

> Upload one PDF bank statement. The bookkeeping period must be monthly. Only posted transactions are imported after statement validation succeeds.

Upload one PDF statement for a custom account using a monthly bookkeeping period. The statement must pass validation before its posted transactions are imported; pending transactions are not imported. Re-uploading the same PDF returns a 400 error, even if the filename changes, and does not create another import.

To remove its imported transactions, use [Archive bank statement upload](/api-reference/v1/archive-transaction-upload).

Find a monthly `bookkeeping_period_id` with [List bookkeeping periods for a business](/api-reference/v1/bookkeeping/list-bookkeeping-periods-for-a-business). Send `import` as a JSON object in the multipart request alongside the PDF; the request itself is not an `application/json` body. For example:

```bash theme={null}
curl --request POST \
  --url "https://sandbox.layerfi.com/v1/businesses/$BUSINESS_ID/custom-accounts/$CUSTOM_ACCOUNT_ID/bank-statement-import" \
  --header "Authorization: Bearer $TOKEN" \
  --form 'import={"bookkeeping_period_id":"BOOKKEEPING_PERIOD_UUID"}' \
  --form 'file=@statement.pdf;type=application/pdf'
```


## OpenAPI

````yaml post /v1/businesses/{businessId}/custom-accounts/{customAccountId}/bank-statement-import
openapi: 3.0.1
info:
  title: API
  version: latest
servers: []
security:
  - BearerAuth: []
tags: []
externalDocs:
  url: /
paths:
  /v1/businesses/{businessId}/custom-accounts/{customAccountId}/bank-statement-import:
    post:
      tags: []
      summary: Import bank statement
      description: >-
        Upload one PDF bank statement. The bookkeeping period must be monthly.
        Only posted transactions are imported after statement validation
        succeeds.
      operationId: business.custom-accounts.bank-statement-import.post
      parameters:
        - name: businessId
          in: path
          description: The UUID of the business.
          required: true
          schema:
            type: string
            format: uuid
        - name: customAccountId
          in: path
          description: The UUID of the custom account.
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                  description: >-
                    One PDF bank statement (up to 10 MB). Send it in the `file`
                    field.
                import:
                  type: object
                  description: Import details sent as JSON in the `import` multipart field.
                  properties:
                    bookkeeping_period_id:
                      type: string
                      format: uuid
                      description: The UUID of a monthly bookkeeping period.
                  required:
                    - bookkeeping_period_id
              required:
                - file
                - import
            encoding:
              import:
                contentType: application/json
      responses:
        '201':
          description: Statement imported and posted transactions created.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/BankStatementImportResult'
                required:
                  - data
        '400':
          description: >-
            Invalid multipart request, PDF parsing failure, duplicate statement,
            or failed statement validation. Validation errors include the parsed
            statement and checks in error metadata.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '404':
          description: Business, custom account, or bookkeeping period not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
      deprecated: false
components:
  schemas:
    BankStatementImportResult:
      type: object
      properties:
        custom_account_id:
          type: string
          format: uuid
        upload_id:
          type: string
          format: uuid
        statement:
          $ref: '#/components/schemas/ImportedBankStatement'
        created_transaction_count:
          type: integer
          description: Number of posted transactions created.
        pending_transaction_count:
          type: integer
          description: >-
            Number of pending transactions on the statement; these are not
            imported.
        checks:
          type: array
          items:
            $ref: '#/components/schemas/BankStatementImportCheck'
      required:
        - custom_account_id
        - upload_id
        - statement
        - created_transaction_count
        - pending_transaction_count
        - checks
    ApiError:
      type: object
      description: An error object returned in API error responses.
      properties:
        type:
          $ref: '#/components/schemas/ApiErrorType'
          description: >-
            A fixed category for the error, helpful for categorizing and
            processing errors.
        description:
          type: string
          description: A human-readable error description.
        error_enum:
          $ref: '#/components/schemas/ApiEnumErrorType'
          description: >-
            A stable, machine-readable identifier for programmatically handling
            specific error conditions. Only present for 4xx client errors—not
            included for 5xx server errors. Use this instead of parsing the
            description field, as enum values remain stable across API versions.
          nullable: true
        meta:
          type: object
          description: Optional additional information about the error.
          nullable: true
      required:
        - type
        - description
    ImportedBankStatement:
      type: object
      properties:
        starting_balance:
          $ref: '#/components/schemas/BankStatementImportBalance'
        ending_balance:
          $ref: '#/components/schemas/BankStatementImportBalance'
        transactions:
          type: array
          items:
            $ref: '#/components/schemas/BankStatementImportTransaction'
      required:
        - starting_balance
        - ending_balance
        - transactions
    BankStatementImportCheck:
      type: object
      properties:
        name:
          type: string
          enum:
            - POSTED_TRANSACTIONS_PRESENT
            - TRANSACTION_AMOUNTS_NON_NEGATIVE
            - STATEMENT_INTERNAL_BALANCE_CONSISTENCY
            - TRANSACTION_DATES_WITHIN_STATEMENT_DATES
            - STATEMENT_DATES_WITHIN_CUSTOM_ACCOUNT_DATES
            - BALANCE_DATES_WITHIN_BOOKKEEPING_PERIOD
            - ACCOUNT_BALANCE_CONTINUITY
        status:
          type: string
          enum:
            - passed
            - failed
            - skipped
        details:
          type: string
        violating_transactions:
          type: array
          items:
            $ref: '#/components/schemas/BankStatementImportTransaction'
      required:
        - name
        - status
        - details
        - violating_transactions
    ApiErrorType:
      type: string
      enum:
        - ResourceArchived
        - AuthFailure
        - Plaid
        - Stripe
        - InvalidState
        - ResourceNotFound
        - InvalidParameters
        - JsonSerialization
        - Unknown
        - BadRequest
        - PaginationCursor
        - Conflict
        - LedgerOperationFailed
      example: InvalidParameters
    ApiEnumErrorType:
      type: string
      description: >-
        Stable enum values for programmatic error handling. Only present in 4xx
        error responses.
      enum:
        - AccessCodeInvalid
        - BalanceSheetDoesNotBalance
        - BalanceSheetMissingAccount
        - BankStatementAccountDetectionError
        - BankStatementParsingError
        - BankStatementParserError
        - BankStatementValidationError
        - BillStateError
        - BulkCategorizeFailure
        - BulkMatchFailure
        - BusinessTaskAlreadyCompleted
        - BusinessTaskDeleted
        - CalendlyOAuthError
        - CallBookingError
        - CantUpdateTransactionInCustomerPayout
        - CantUpdateTransactionInVendorPayout
        - CheckPayrollConfigNotFound
        - CheckPayrollServiceNotFound
        - ClerkUserAlreadyExists
        - ConflictingQueryParams
        - CustomAccountAlreadyExists
        - CustomTransactionCsvParsingError
        - CustomTransactionUploadFailure
        - CustomerPayoutInputFormatError
        - DoesNotMatchExistingEntity
        - EmptyBatchRequest
        - ExpenseParserError
        - ExternalAccountBalanceReconciliationError
        - ExternalIdConflict
        - InvalidCategory
        - InvalidEffectiveDate
        - InvalidLedgerOperation
        - InvalidMonthlyAverageRange
        - InvalidMultiPartRequest
        - InvalidPaginationCursor
        - InvalidPayload
        - InvoiceDeleted
        - InvoiceNotFound
        - InvoiceReferenceMismatch
        - InvoiceStateError
        - ManualRateLimit
        - MultipleTagKeyFiltersUnsupported
        - NoCognitoUserFound
        - NoOpeningBalanceFound
        - NotYetReconciled
        - OnePasswordApiError
        - OnePasswordItemNotFound
        - OnePasswordVaultNotFound
        - OpenAICategorizationError
        - PaymentLinkInvalid
        - PayrollStateError
        - PeriodIsClosed
        - PeriodNotClosed
        - PhoneNumberAlreadyRegistered
        - PlaidApiError
        - PlaidConnectionBroken
        - PlaidCreateLinkTokenError
        - PlaidCredentialsNotConfigured
        - PlaidExchangePublicTokenError
        - PlaidGetInstitutionByIdError
        - PlaidGetItemError
        - PlaidInvalidEnvironment
        - PlaidItemAlreadyExists
        - PlaidItemNotFound
        - PlaidProcessorApiError
        - PlaidUnlinkItemError
        - QueryParamFormat
        - QueryParamMissing
        - QuickbooksBrokenConnection
        - QuickbooksConnectionAlreadyExists
        - QuickbooksConnectionAlreadySyncing
        - QuickbooksConnectionMissing
        - QuickbooksConnectionNotActivated
        - QuickbooksInvalidRequest
        - QuickbooksInvalidState
        - QuickbooksNoMatchingAccount
        - QuickbooksNonPostingAccountType
        - QuickbooksNotConfigured
        - QuickbooksOAuthCallbackInvalid
        - QuickbooksOAuthError
        - QuickbooksTokenExpired
        - ReadOnlyBusiness
        - ResourceArchived
        - ScheduleCNotConfigured
        - SmsNotEnabled
        - SpecifiedBadRequest
        - SpecifiedIdNotFound
        - SplitTransactionError
        - StepEvaluationBadRequest
        - StripeConnectAccountIdNotFound
        - StripeCredentialsNotConfigured
        - StripeGetBalanceForConnectAccountFailure
        - StripeRedirectOrRefreshUrlNotConfigured
        - TagFilterNotFound
        - UnexpectedQueryParam
        - UnitAccountsInUse
        - WrongAnswerType
      example: InvalidPayload
    BankStatementImportBalance:
      type: object
      properties:
        date:
          type: string
          format: date
        amount:
          type: integer
          format: int64
          description: Balance in cents.
      required:
        - date
        - amount
    BankStatementImportTransaction:
      type: object
      properties:
        date:
          type: string
          format: date
        amount:
          type: integer
          format: int64
          description: Transaction amount in cents.
        direction_reason:
          type: string
          description: Explanation of the parsed direction; may be empty.
        direction:
          type: string
          enum:
            - CREDIT
            - DEBIT
        status:
          type: string
          enum:
            - POSTED
            - PENDING
        description:
          type: string
      required:
        - date
        - amount
        - direction_reason
        - direction
        - status
        - description
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

````