> ## 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.

# Update Transaction API

> Update a single unlocked Breezing transaction by ID to patch crypto accounting fields without changing other transaction values.



## OpenAPI

````yaml PATCH /transactions/{id}
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/{id}:
    patch:
      tags:
        - Transactions
      summary: Update a single transaction
      description: >-
        Update accounting and financial fields on a single unlocked transaction
        (partial update: only provided fields change). Returns 409 if locked or
        processing.


        FINANCIAL FIELDS (date, amount, fee, token, direction, amountFiat,
        feeFiat):

        • Provide amounts as POSITIVE decimals; the server derives signs from
        the effective direction (positive for "in", negative for "out"; fee and
        feeFiat are forced "0" on "in" rows). "date" is unix SECONDS.

        • Changing direction re-signs the stored amount/fee/amountFiat/feeFiat
        automatically when you do not pass them explicitly, and returns 409 when
        it would flip an exchange trade leg (exchangeId set) or an internal
        transfer leg (isInternal=true); ungroup a trade first via POST
        /transactions/ungroup-trades.

        • These fields change ledger arithmetic. Balances, cost basis and net
        gain/loss are NOT recomputed automatically: run POST /balances/calculate
        and POST /ngl/calculate afterwards. Changing amount does not recompute
        amountFiat; pass it explicitly or run POST
        /transactions/update-price-auto. 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.


        FULLY CATEGORIZING A TRANSACTION requires three account fields:

        • "account": the contra (income/expense) account. What was this
        transaction for? (e.g. Sales, Legal Expense, Consulting Revenue). Choose
        based on type, functionName, walletFromName/walletToName context.

        • "assetAccount": the balance-sheet asset account. Where does this token
        sit on the balance sheet? Required in ALL balance sheet modes. If the
        value is "(auto)", Breezing resolves it at sync time from token-level
        mappings (set via assets_update) or rule-based mappings. You can leave
        "(auto)" as-is or override with a specific account code.

        • "feeAssetAccount": the balance-sheet account for the fee portion.
        Always set this field (typically the same account as assetAccount). If
        "(auto)", Breezing resolves it at sync time.


        All three fields accept numeric account codes from the chart of accounts
        (call company_get first). Never pass free-text names like "sales".


        Categorization workflow:

        1. Set "account" (contra account: what was the transaction for?)

        2. Check assetAccount. If "(auto)", leave as-is when token-level
        mappings exist (Token View), or set explicitly for Wallet/Combined View.

        3. Set feeAssetAccount; always set this field (same account as
        assetAccount in most cases).

        4. Optionally set "accountingDescription" for journal entry context.

        Do NOT automatically change status. Leave it for the user to decide when
        the transaction is ready for reconciliation.


        How balanceSheetSetup affects asset accounts:

        • "token" (Token View) → asset accounts are managed at the token level
        via assets_update. Transactions show "(auto)" which resolves to the
        token's mapped account at sync time. assets_update sets the mapping;
        "(auto)" on transactions references it. They are not competing.

        • "wallet" (Wallet View) → asset accounts are set per-transaction,
        usually via Rules. Each wallet maps to one GL account.

        • "wallet-token" (Combined View) → asset accounts are set
        per-transaction, usually via Rules with wallet+token conditions.


        Other fields:

        • "type": freeform label, safe to overwrite. System may set values like
        "transaction" or "internal" during import, but these are just labels,
        not business logic. The system uses the isInternal boolean (not type)
        for internal transfer logic. Set to any descriptive value.

        • "status": lifecycle: notStarted (new/uncategorized) → reviewRequired
        (flagged for client review via the Client Review workflow) → reconciled
        (categorized and verified, ready for sync) → manuallyReconciled
        (manually confirmed) → paid (synced to Xero/QuickBooks/Bexio). Do not
        change status automatically; leave for the user.

        • "accountingDescription": syncs as the journal entry description in
        Xero/QuickBooks/Bexio. Use for invoice numbers, vendor names, or payment
        references. Distinct from "note" which is internal only.

        • "isSpam": mark airdrop/scam tokens as spam to hide them. Breezing
        auto-detects many common spam tokens; use this for any missed.

        • "vat": 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.

        • "skipNgl" / "skipNglReason": set skipNgl=true to exclude this
        transaction from net gain/loss (NGL) and tax calculations (e.g.
        wrapping, staking, bridging). skipNglReason is REQUIRED when
        skipNgl=true. Cannot be set on internal transfers (isInternal=true).
        Setting skipNgl does NOT recalculate NGL; call POST /ngl/calculate
        afterward to recompute net.

        • "qboClass" / "xeroTrackingCategory": accounting-software tracking
        dimensions. Set the QuickBooks class id (from POST /qbo/classes/refresh)
        or the Xero tracking category option id (from POST
        /xero/categories/refresh). Only relevant when that platform is linked.


        IMPORTANT, internal transfers and exchange trades:

        • If isInternal=true (system-computed), the transaction is a transfer
        between the user's own wallets. Recommend using Breezing's Auto Apply
        Account feature instead of manual assignment.

        • If exchangeId is a non-null number (system-set), the transaction is an
        exchange trade. Also recommend Auto Apply Account.

        • Note: isInternal and type are independent. A transaction can have
        isInternal=false with type="internal". The system uses isInternal for
        business logic, not type.
      parameters:
        - schema:
            type: string
            description: Transaction ID
            example: '12345'
          required: true
          description: Transaction ID
          name: id
          in: path
        - 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/PatchTransactionBody'
      responses:
        '200':
          description: Transaction updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      updated:
                        type: integer
                    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: Transaction 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: Transaction is 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:
    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.