balanceTransactions GraphQL API returns wrong type for Managed Markets charges — returns ADJUSTMENT instead of CHARGE


Summary

The shopifyPaymentsAccount.balanceTransactions GraphQL API returns type: ADJUSTMENT for transactions that are actual customer charges processed through Shopify Managed Markets (international currency conversion). The Shopify Admin UI and CSV export both correctly show these as type: charge, making the GraphQL API the only source that misclassifies them. This discrepancy breaks financial reconciliation systems built on the API.


Steps to Reproduce

  1. Have a store with Managed Markets enabled (international orders settled in a different currency — in our case, AUD presentment → USD settlement).
  2. Query balanceTransactions filtered by a payout ID that contains such charges:
  query GetPayoutTransactions($limit: Int!, $after: String, $queryFilter: String!) {
    shopifyPaymentsAccount {
      balanceTransactions(first: $limit, after: $after, query: $queryFilter) {
        edges {
          node {
            id
            type
            sourceType
            adjustmentReason
            associatedOrder { id name }
            amount { amount currencyCode }
            fee { amount currencyCode }
            net { amount currencyCode }
          }
        }
      }
    }
  }

Variables:

{ "limit": 50, "queryFilter": "payments_transfer_id:141473382555" }

Actual vs Expected Behavior

Source Order Type Amount
Shopify Admin (Payments → Balance) WS113833 Charge $98.33
Shopify CSV Export WS113833 charge $98.33
GraphQL balanceTransactions WS113833 ADJUSTMENT :cross_mark: $98.33

The GraphQL response for this transaction:

  {
    "type": "ADJUSTMENT",
    "sourceType": "ADJUSTMENT",
    "adjustmentReason": "managed_markets_charge_credit",
    "associatedOrder": {
      "id": "gid://shopify/Order/6251766677659",
      "name": "WS113833"
    },
    "amount": { "amount": 98.33, "currencyCode": "USD" },
    "fee":   { "amount": 8.97,  "currencyCode": "USD" },
    "net":   { "amount": 89.36, "currencyCode": "USD" }
  }

This is a real customer charge (Visa, AUD 151.00 → USD 98.33). It should be type: CHARGE, not ADJUSTMENT.


Root Cause Analysis

It appears Shopify internally represents Managed Markets currency-conversion charges as an adjustment entry (adjustmentReason: managed_markets_charge_credit) in the balance ledger, but correctly surfaces them as Charge in the Admin UI and CSV export. The GraphQL API is leaking this internal ledger classification instead of returning the merchant-facing transaction type.


Business Impact

This bug has significant financial consequences for merchants building on the API:

  • Revenue misclassified as adjustments: Our automated financial reconciliation system, built on the balanceTransactions API, categorizes all Managed Markets charges as ADJUSTMENT instead of CHARGE. This makes revenue reporting inaccurate.
  • Reconciliation failures: Our system skips or flags these transactions differently based on type, causing mismatches between our internal records and Shopify’s payout reports.

We are unable to accurately reconcile Shopify Payments payouts programmatically until this is fixed. The CSV export workaround is not viable for automated systems processing high transaction volumes.


Environment

  • API Version: 2025-07
  • Affected transaction type: Managed Markets charges (foreign currency resentment → USD settlement)
  • adjustmentReason value: managed_markets_charge_credit

Thank you for any help.

Hey @Yinsen appreciate the really clear write-up, thank you. This looks like a genuine API/UI discrepancy and I want to flag it internally.

You’ve correctly identified the shape of it: the Admin UI and CSV export classify Managed Markets currency-conversion charges as Charge, while balanceTransactions returns type: ADJUSTMENT with adjustmentReason: managed_markets_charge_credit. Since adjustmentReason is documented as null when sourceType isn’t an adjustment, the API is treating these as ledger adjustments, even though they represent customer-initiated charges to the merchant-facing view.

I don’t have a confirmation that this is officially classified as a bug yet — I’ll raise it with the Payments + GraphQL teams referencing this thread and the example transaction. Will follow up here when I have something back.

In the meantime — workaround for reconciliation

Until this is corrected, you can normalise on the client side. The combination of type: ADJUSTMENT + adjustmentReason: "managed_markets_charge_credit" + non-null associatedOrder is a reliable signature for “this is actually a charge”:

function normalizedType(txn) {
  if (
    txn.type === "ADJUSTMENT" &&
    txn.adjustmentReason === "managed_markets_charge_credit" &&
    txn.associatedOrder
  ) {
    return "CHARGE";
  }
  return txn.type;
}

Worth widening the check if you also see refunds for these transactions — managed_markets_charge_debit or similar may appear and would map to REFUND in the merchant-facing view. If you spot other adjustmentReason values in your data that look like real charges/refunds, drop them in this thread and I’ll include them when I escalate.