API failing on Unit Cost, Category and Inventory counts

Title: Silent field drops on UpsertCatalogObject (default_unit_cost, category_id) + 404s on batch/inventory endpoints

Hi, I’m seeing several Catalog and Inventory API issues on Square-Version 2026-08-19 that I’m reporting together in case they’re related.

1. default_unit_cost silently ignored on UpsertCatalogObject
POST /v2/catalog/object with a full item object including an updated default_unit_cost on the variation returns 200 OK, but the cost is not applied — no version bump, old value on echo and read-back. Worse, a partial-payload upsert (variation data only) returned 200 OK but cleared the existing cost entirely; I restored it with a full-object write and verified. I have support case #122025325 open on this.

2. category_id silently ignored on UpsertCatalogObject
Same pattern, found today: POST /v2/catalog/object with category_id set on item_data returns 200 OK, no errors, but category_id stays null on read-back and the object version doesn’t change — the write is a complete no-op. Notably, tax_ids in the same payload applied fine, and description writes work too, so it’s field-specific, not a general write failure.

3. All /v2/catalog/batch- endpoints return 404*
batch-upsert, batch-retrieve, and batch-delete all return 404 NOT_FOUND regardless of API version. Single-object endpoints (POST /v2/catalog/object, GET /v2/catalog/object/{id}, POST /v2/catalog/search) work.

4. Inventory writes return 404
POST /v2/inventory/changes returns 404 NOT_FOUND. Inventory reads (POST /v2/inventory/counts/batch-retrieve) work fine.

Repro for the silent drops: snapshot the object, deep-copy, change only the one field, upsert with the current version, read back. The response echo shows the old value and the version is unchanged.

Anyone else hitting the category_id drop? Any known timeline on the cost write fix?

Welcome to the Developer Forum!

  1. default_unit_cost: this field isn’t officially documented or supported for writes yet, so upsert behavior isn’t guaranteed. To set a unit cost, use CatalogItemVariationVendorInformation.unit_cost_money. (CatalogItemVariationVendorInformationBeta) Also, UpsertCatalogObject should be treated as replacing the object, so retrieve the current object, update it, include the current version, and send the full object rather than a partial nested payload.
  2. category_id: this field has been deprecated since 2023-12-13. Set item_data.categories (and item_data.reporting_category if you use reporting or category-based features) instead. (CatalogItem)
  3. Catalog batch 404s: batch-upsert, batch-retrieve and batch-delete are all POST to /v2/catalog/batch- endpoints. I was not able to replicate this on my end. If this issue still persists, please share the exact method, full URL and raw response for one failing call, and we’ll take a look.
  4. Inventory writes: POST /v2/inventory/changes isn’t an endpoint. Use POST /v2/inventory/changes/batch-create. (BatchChangeInventory)

Hope this helps!

Thanks for the detailed response — 3 of 4 are resolved:

  1. Category: switched to item_data.categories, working.
  2. Batch endpoints: confirmed working on my end now.
  3. Inventory: POST /v2/inventory/changes/batch-create with state: "IN_STOCK" works. (We were on the wrong endpoint.)

Costs are still stuck. I tested both paths exactly as described:

  • default_unit_cost via full-object UpsertCatalogObject (retrieve current, update one field, include current version, send full object): returns 200, version bumps, but the value doesn’t stick on read-back.
  • CatalogItemVariationVendorInformation.unit_cost_money: also silently dropped — version doesn’t even bump.

One thing that makes me think something is off: I can update unit cost through the POS app, so the data is writable on my plan. If the API truly requires a higher tier, can you confirm that definitively? Or is there another workaround I’m missing?

Thanks.

I also notice that the Vendor isn’t reliably being updated via API - any timeframe for that to be workable? Thanks!

Thanks for following up and confirming what’s working.

default_unit_cost is not a documented/supported writable Catalog field, so it isn’t expected to reliably persist through UpsertCatalogObject.

unit_cost_money field on CatalogItemVariationVendorInformation is currently Beta, and the docs state that CatalogItemVariation.vendor_information can be set only if you are an active Square for Retail Premium or Square for Restaurants Premium subscription. Are you using one of these subscriptions?

I see. We have a square for retail plus plan and can set the unit and vendor fields on the POS or Dashboard so I assumed that it would work with the API.

So if I were to upgrade to Premium it would work? It does say Vendor and unit cost is covered with my current plan.

Thanks for letting me know what plan you have.

Before we make any change to this, let’s go through this step by step one more time.

Please test with item_data.variations.item_variation_data.vendor_information.unit_cost_money, not default_unit_cost, using a full POST /v2/catalog/object payload with the current object version.

Then, share the request/response for that upsert plus the read-back response from GET /v2/catalog/object/{ITEM_VARIATION_ID}? With those IDs/details, I can investigate further.

Tested full POST /v2/catalog/object with the current object version, setting variation-level vendor_information to {“vendor_id”: “HPXIMUDLKN57IB4W”, “unit_cost_money”: {“amount”: 1597, “currency”: “USD”}} on variation L5YPTBROSVNDPWLZFNBAI4JZ (parent item LMSC2R7Z77VTXULLY5MYQ2RB).

Upsert response: vendor_information came back null and the version did not change (1790904747667 before and after). Read-back via GET /v2/catalog/object/{item_id} also shows vendor_information null on the variation.

So the write is accepted with 200 OK but silently ignored. Note: your path nests vendor_information inside item_variation_data, but the documented schema places it at the variation level (CatalogItemVariation.vendor_information), which is where I set it. I have the full request/response and read-back JSON saved if you want it.

I found two unit_cost_money values in your request.

The correctly placed field, item_variation_data.vendor_information.unit_cost_money, contains 1448. The intended 1597 is in a sibling vendor_information object outside item_variation_data, which is not the documented location.

Could you retry with 1597 in the item_variation_data.vendor_information array and remove the sibling object? Please share the upsert response and read-back so we can check whether the corrected write persists.

I would love to ensure we settle on the correct request format before I suggest bumping up your plans.