TLDR: QuickBooks Online's vendor merge is UI-only. The API rejects it with code 6240. Fix: loop your dupes, fetch each transaction, and sparse-update the
EntityRefto point at the canonical vendor. Takes 10 minutes. Books stay clean.
The Setup
I've been closing the 2025 books for a cancer education business — using a custom QuickBooks MCP server (MCP = Model Context Protocol, an API layer that lets an AI agent talk to external services) I built.
Fully automated. I could issue a natural-language command and have the agent categorize transactions, pull reports, the whole thing.
Until it couldn't.
The Wall
End of the reconciliation pass, I noticed duplicate vendors in the chart of accounts.
"Take Shape" and "Takeshape." "Claude Ai" and "Anthropic." (Yes, I had a vendor called "Claude Ai." No, I don't fully understand how that happened.)
In the QuickBooks Online UI, this is a two-click fix. Rename the duplicate to match the canonical name and QBO surfaces a merge prompt: "This name already exists. Merge?" Click yes. Done.
So I tried the same thing via update-vendor in the MCP.
ValidationFault — "Duplicate Name Exists Error" (code 6240).
The API straight-up refuses. No merge. No prompt. Just a hard rejection.
Why It Fails
The QBO UI's "merge" feature isn't one atomic operation under the hood.
It's a macro — QBO sees the rename collision and implicitly reassigns every transaction pointing at the old vendor to the surviving one, then retires the husk. The API exposes the primitives, not the macro. So when you try to rename to a colliding name via the API, you just get a validation error. There's nothing to catch it and run the reassignment for you.
The UI convenience button is not available at the API layer. You have to decompose it yourself.
The Fix That Worked
For each duplicate vendor:
- Enumerate its transactions — pull the general ledger for the relevant account, filter by vendor name client-side
- Fetch each transaction live for its current
SyncToken(required for any write — QBO will reject stale tokens) - Sparse-update the
EntityRefto point at the canonical vendor
The sparse update payload looks like this:
{
"Id": "...",
"SyncToken": "...",
"sparse": true,
"EntityRef": { "value": "294", "name": "Takeshape" }
}
Echo back AccountRef, PaymentType, Credit, TxnDate, CurrencyRef, and the Line array unchanged. Just swap the EntityRef. Done.
What this does NOT touch: amounts, dates, cleared status, the bank/CC-account side. Reconciliation is anchored to those fields — a payee change is pure metadata. I checked this before running it against financial records. None of the 2025 numbers moved; net income held steady.
The One Catch
The emptied duplicate vendor doesn't disappear. It survives empty — 0 transactions. And the MCP's update-vendor doesn't expose an Active field, so I can't deactivate it programmatically.
One manual click in the QBO UI to mark it inactive. Not ideal. But acceptable.
Why This Matters to Me
This shows up constantly with accounting APIs: the UI has a convenience button that wraps several primitives into one action. The API gives you the primitives. When you hit a wall, the question isn't "how do I call the merge endpoint?" — it's "what is the merge button actually doing, and can I do those steps myself?"
Usually you can.