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

# Batch Update Transactions API

> Update Breezing transactions in bulk with one API request to apply accounting field changes across unlocked crypto accounting records.



## OpenAPI

````yaml PATCH /transactions
openapi: 3.1.0
info:
  title: Breezing Public API
  version: 1.0.0
  description: >-
    Crypto accounting API for programmatic access and AI agents. Authenticate
    with an API key using the Authorization header: `Bearer brz_...`
servers:
  - url: https://api.breezing.io/v1
security:
  - BearerAuth: []
paths:
  /transactions:
    patch:
      tags:
        - Transactions
      summary: Batch update transactions
      description: >-
        Batch update accounting and financial fields on up to 500 unlocked
        transactions. Same fields and rules as transactions_update, applied
        uniformly to all IDs. Returns 409 if any transaction is locked or
        processing.


        FINANCIAL FIELDS (date, amount, fee, token, direction, amountFiat,
        feeFiat): the same positive-decimal values apply to every row and the
        sign is derived per row from its effective direction (a batch can mix
        "in" and "out" rows). Changing direction requires explicit amount and
        amountFiat (plus fee and feeFiat when direction="out") and returns 409
        when it would flip an exchange trade leg or an internal transfer leg.
        Balances, cost basis and net gain/loss are NOT recomputed automatically:
        run POST /balances/calculate and POST /ngl/calculate afterwards. Only
        edit financial data when the user explicitly asks for a correction.


        LEDGER IDENTITY FIELDS (chain, feeToken, tokenAddress, feeTokenAddress,
        walletFrom, walletTo, transactionId, currency): plain column writes with
        no sign handling. walletFrom/walletTo are counterparty addresses: the
        contact link behind walletFromName/walletToName is re-resolved the way
        imports do (the company contact with the same address, case-insensitive,
        else a contact is created for it and named after the company wallet when
        the address is one of the company's own wallets; name other new contacts
        via PATCH /contacts/{id}), and internal transfer detection is NOT re-run
        (use POST /transactions/group-internals). Changing chain or feeToken
        moves the row between per-chain and per-token balances: run POST
        /balances/calculate afterwards.


        This is the API equivalent of applying a Rule in the Breezing UI. Use it
        when you identify a pattern across transactions (same wallet, same
        token, same counterparty) that should all get the same account. Skip
        transactions where isInternal=true or exchangeId is a non-null number;
        those should use Auto Apply Account instead.


        When setting accounts, first call company_get and use numeric account
        codes.
      parameters:
        - schema:
            type: string
            description: >-
              Organization ID. Use GET /v1/companies to discover available
              org/company pairs.
            example: '1'
          required: true
          description: >-
            Organization ID. Use GET /v1/companies to discover available
            org/company pairs.
          name: orgId
          in: query
        - schema:
            type: string
            description: >-
              Company ID. Use GET /v1/companies to discover available companies
              and their access levels.
            example: '1'
          required: true
          description: >-
            Company ID. Use GET /v1/companies to discover available companies
            and their access levels.
          name: companyId
          in: query
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchPatchTransactionsBody'
      responses:
        '200':
          description: Transactions updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      updated:
                        type: integer
                      dedupedFrom:
                        type: integer
                        description: >-
                          Original input length when duplicate IDs were deduped
                          server-side. Omitted when the input had no duplicates.
                    required:
                      - updated
                required:
                  - success
                  - data
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - false
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - success
                  - error
        '401':
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - false
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - success
                  - error
        '403':
          description: >-
            API key has no access to this company, has read-only access where
            write was required, or the company subscription is inactive
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - false
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - success
                  - error
        '404':
          description: One or more transactions were not found
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - false
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - success
                  - error
        '409':
          description: One or more transactions are locked or currently processing
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - false
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - success
                  - error
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - false
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - success
                  - error
      security:
        - BearerAuth: []
components:
  schemas:
    BatchPatchTransactionsBody:
      type: object
      properties:
        ids:
          type: array
          items:
            type: integer
          minItems: 1
          maxItems: 500
          description: Transaction IDs to update (max 500)
        updates:
          allOf:
            - $ref: '#/components/schemas/PatchTransactionBody'
            - description: Fields to update
      required:
        - ids
        - updates
    PatchTransactionBody:
      type: object
      properties:
        account:
          type: string
          nullable: true
          description: >-
            Account code (numeric string). The contra (income/expense) account:
            what was this transaction for? (e.g. Sales, Legal Expense,
            Consulting Revenue). IMPORTANT: Always call company_get first to
            retrieve the chart of accounts, find the account by name, and use
            its "code" field. Never pass a free-text name like "sales".
        assetAccount:
          type: string
          nullable: true
          description: >-
            Asset account code (numeric string). The balance-sheet account for
            the token in this transaction. Required in ALL balance sheet modes.
            If the current value is "(auto)", Breezing resolves it at sync time
            from token-level or rule-based mappings. You can leave it or
            override with a specific code. IMPORTANT: Always call company_get
            first to retrieve the chart of accounts and use the numeric code.
        feeAssetAccount:
          type: string
          nullable: true
          description: >-
            Fee asset account code (numeric string). The balance-sheet account
            for the fee portion. Same rules as assetAccount: required in ALL
            balance sheet modes. If "(auto)", Breezing resolves it at sync time.
            IMPORTANT: Always call company_get first to retrieve the chart of
            accounts and use the numeric code.
        status:
          type: string
          enum:
            - notStarted
            - reviewRequired
            - reconciled
            - manuallyReconciled
            - paid
            - synced
          description: >-
            Transaction status lifecycle: notStarted (new/uncategorized) →
            reviewRequired (flagged for client review; client sees these in the
            Client Review workflow) → reconciled (categorized and verified) →
            manuallyReconciled (manually confirmed) → paid (synced to accounting
            software). Set to reviewRequired when the accountant needs the
            client to clarify a transaction.
        type:
          type: string
          nullable: true
          description: >-
            Transaction type as a freeform label, safe to overwrite. System may
            set values like "transaction" or "internal" during import, but these
            are just labels, not business logic. The isInternal boolean controls
            actual internal transfer logic. Set to any descriptive value:
            "Opening Balance", "Staking Reward", "Payment", etc.
        note:
          type: string
          nullable: true
          description: >-
            User note. Internal only, not synced to accounting software. Use
            accountingDescription for text that should appear in journal
            entries.
        accountingDescription:
          type: string
          nullable: true
          description: >-
            Description that syncs as the journal entry description in
            Xero/QuickBooks/Bexio. Use for invoice references, vendor names, or
            transaction context that should appear in the accounting software.
            Placeholders {{externalWalletName}}, {{externalWallet}} (the
            counterparty: walletTo on an out row, walletFrom otherwise),
            {{walletFromName}}, {{walletToName}}, {{walletFrom}}, {{walletTo}},
            {{token}}, {{amount}}, {{transactionId}} and {{date}} are filled
            when the journal is generated. Distinct from "note" which is
            internal only.
        vat:
          type: string
          nullable: true
          description: >-
            VAT or sales tax code as a freeform string that syncs to journal
            entries. Must match a tax rate name configured in the user's
            Xero/QuickBooks/Bexio account (e.g. "Output 8.1% VAT", "Tax
            Exempt"). Default is "no-vat". No validation, so invalid codes may
            cause sync errors.
        isSpam:
          type: boolean
          description: >-
            Spam flag. Set true to hide airdrop/scam tokens from the main view
            and prevent syncing to accounting software. Breezing auto-detects
            many spam tokens; use this for any it missed.
        extraLabel:
          type: string
          nullable: true
          description: >-
            Extra label: freeform text for custom tagging or grouping, visible
            in Breezing reports.
        skipNgl:
          type: boolean
          description: >-
            Skip net gain/loss. When true, realized gain/loss is zeroed for this
            transaction (excluded from NGL and tax calculations). Use for
            non-taxable movements like wrapping, staking, or bridging. Requires
            skipNglReason when set to true. Cannot be set on internal transfers
            (isInternal=true). Setting this does NOT recalculate NGL
            automatically; run POST /ngl/calculate afterward to recompute net.
        skipNglReason:
          type: string
          nullable: true
          description: >-
            Reason net gain/loss is skipped (e.g. "Wrapping", "Staking", "Bridge
            transfer"). Required when skipNgl is set to true.
        qboClass:
          type: string
          nullable: true
          description: >-
            QuickBooks Online class ID. Syncs as the class on the journal entry
            in QuickBooks. Use the "id" returned by the QuickBooks classes
            refresh endpoint (call it first to retrieve valid class IDs). Only
            relevant when QuickBooks is linked.
        xeroTrackingCategory:
          type: string
          nullable: true
          description: >-
            Xero tracking category option ID. Syncs as the tracking category on
            the journal entry in Xero. Use the "id" returned by the Xero
            categories refresh endpoint (call it first to retrieve valid option
            IDs). Only relevant when Xero is linked.
        date:
          type: integer
          minimum: 0
          exclusiveMinimum: true
          description: >-
            New transaction timestamp in unix SECONDS (not milliseconds).
            CAUTION: dates drive cost-basis lot ordering (FIFO/LIFO/HIFO consume
            lots in date order) and an internal transfer IN leg must stay dated
            after its OUT leg. Net gain/loss is NOT recomputed automatically;
            run POST /ngl/calculate afterwards.
          example: 1700000000
        direction:
          type: string
          enum:
            - in
            - out
          description: >-
            New direction. "in" = the wallet received the asset; "out" = the
            wallet sent it. The server re-signs amount/fee/amountFiat/feeFiat to
            match the new direction: values you pass explicitly are taken as
            positive decimals and signed server-side, values you omit are
            re-signed from their stored magnitudes (fee and feeFiat are forced
            "0" when the new direction is "in"). Returns 409 when the change
            would flip an exchange trade leg (exchangeId set) or an internal
            transfer leg (isInternal=true); ungroup a trade first via POST
            /transactions/ungroup-trades. In the batch endpoint, changing
            direction additionally requires explicit amount and amountFiat (plus
            fee and feeFiat when direction="out").
        amount:
          anyOf:
            - type: string
            - type: number
          description: >-
            New token amount as a POSITIVE decimal (string preferred for
            precision). The server signs it from the effective direction:
            positive for "in", negative for "out". Changing amount does NOT
            recompute amountFiat or net gain/loss; pass amountFiat too (or run
            POST /transactions/update-price-auto) and run POST /ngl/calculate
            afterwards.
          example: '1.5'
        fee:
          anyOf:
            - type: string
            - type: number
          description: >-
            New fee in fee-token units as a POSITIVE decimal. Fees never sit on
            inbound rows: when the effective direction is "in" the server forces
            "0", for "out" it is stored negative.
          example: '0.001'
        token:
          type: string
          minLength: 1
          description: >-
            New token symbol (e.g. "ETH"). Renames the symbol on this row only;
            feeToken and tokenAddress are unchanged.
          example: ETH
        amountFiat:
          anyOf:
            - type: string
            - type: number
          description: >-
            New fiat value of the amount as a POSITIVE decimal. Signed
            server-side from the effective direction, like amount.
          example: '3000.00'
        feeFiat:
          anyOf:
            - type: string
            - type: number
          description: >-
            New fiat value of the fee as a POSITIVE decimal. Forced "0" when the
            effective direction is "in", stored negative for "out", like fee.
          example: '2.00'
        chain:
          anyOf:
            - $ref: '#/components/schemas/ChainValue'
            - type: string
          description: >-
            Blockchain identifier. Prefer one of the supported chain values
            (e.g. "eth", "matic", "solana"; the ChainValue schema lists them):
            they drive explorer links, chain filters and per-chain balances. Any
            other string is accepted and stored as given, for chains Breezing
            does not index. Pass "" to clear it.
          example: eth
        feeToken:
          type: string
          minLength: 1
          description: >-
            Fee token symbol (e.g. "ETH"). Balances debit fees under this
            symbol; it must stay non-empty.
          example: ETH
        tokenAddress:
          type: string
          maxLength: 256
          description: >-
            Token contract address, case preserved (Solana and TRON addresses
            are case sensitive). Address-keyed pricing and trade grouping
            resolve through it. Pass "" to clear it.
          example: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
        feeTokenAddress:
          type: string
          maxLength: 256
          description: Fee token contract address, same rules as tokenAddress.
        walletFrom:
          type: string
          maxLength: 255
          description: >-
            Counterparty address the asset came from. The contact link
            (walletFromId, walletFromName) is re-resolved the way imports do:
            the company contact with the same address (case-insensitive), else a
            contact is created for it (named after the company wallet when the
            address is one of the company's own wallets, otherwise unnamed; name
            it via PATCH /contacts/{id}). Internal transfer detection is NOT
            re-run; use POST /transactions/group-internals. Pass "" to clear the
            address and its link.
        walletTo:
          type: string
          maxLength: 255
          description: >-
            Counterparty address the asset went to. Same contact resolution
            rules as walletFrom.
        transactionId:
          type: string
          minLength: 1
          description: >-
            External reference (blockchain hash, exchange order id, invoice
            number). NOT deduped. In the batch endpoint the same value lands on
            every row.
          example: '0xabc123'
        currency:
          type: string
          minLength: 3
          maxLength: 3
          description: >-
            3-letter fiat currency of amountFiat/feeFiat (e.g. "USD"). A value
            that differs from the company currency surfaces as a currency
            mismatch active issue.
          example: USD
    ChainValue:
      type: string
      enum:
        - eth
        - bsc
        - lyx
        - matic
        - avax
        - abstract
        - arb
        - op
        - base
        - xdai
        - celo
        - katana
        - linea
        - btt
        - ape
        - taiko
        - solana
        - btc
        - bch
        - zec
        - dash
        - doge
        - ltc
        - ada
        - strk
        - trx
        - sui
        - apt
        - blast
        - sei
        - sonic
        - scroll
        - nova
        - era
        - bera
        - stable
        - arc
        - hyperevm
        - mantle
        - fraxtal
        - monad
        - hbar
        - xrp
        - xlm
        - icp
        - xtz
        - ton
        - near
        - plasma
        - unichain
        - world
        - opbnb
        - megaeth
        - xdc
        - immutable
        - mode
        - robinhood
        - lightlink
        - soneium
        - somnia
        - dot
        - tao
      description: >-
        A chain Breezing indexes (e.g. "eth", "matic", "solana"). Preferred
        values for the chain field; other strings are accepted too.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.