Catalog items created via API never appear in the Dashboard item library list (visible in POS app and Dashboard search)

Hi,

I’m running a self-hosted inventory system (FastAPI + SQLite) that syncs products to Square via the Catalog API. I’m seeing two issues I can’t explain, and I’d appreciate any insight.

Environment

  • Square API version: 2025-01-23
  • Production environment
  • Items created via POST /v2/catalog/object (upsert)
  • Single location, present_at_all_locations: true

Issue 1 — Items never appear in the Dashboard item library list

After creating an item via the API, it does not appear in the Dashboard item library list. The exact behavior:

  1. Create item A via the API
  2. The Dashboard item library list shows it immediately after the create action
  3. Reload the browser → the item disappears from the list
  4. The item is still findable via the Dashboard search box
  5. The item is visible in the POS app (iPhone)
  6. ListCatalog and SearchCatalogObjects return it correctly

This is not eventual consistency. I have waited hours and the items never appear in the list view.

How I check Result
POS app (iPhone) Visible
Dashboard search box Visible
Dashboard item library list (after reload) Not visible
ListCatalog / SearchCatalogObjects Returned correctly

Item and variation both have present_at_all_locations: true. present_at_location_ids and absent_at_location_ids are both null.

I’ve tried a different browser, an incognito window, logging out and back in, and a hard reload. No change.

It looks like the list view is built from an index these items never enter, while the post-create render and the search box use a different path. Has anyone run into this? Is there something in the item payload that determines whether an item is included in the list view?


Issue 2 — Duplicate items accumulate, and deletions leave orphans

When I query with include_deleted_objects: true, I find 13+ soft-deleted items sharing the same name, each with a different catalog object ID. Something is repeatedly deleting and recreating the same item.

I also saw the catalog object ID change for what should be the same item:

Earlier: 4OPIUGK724FMOOG344G3ZKHB
Later:   QL6ACNIWQY7PUMJGIHIWLOVW

Because of this, when my system deletes a product it sends a delete for the ID it has on record, but Square holds a different ID — so the item is left behind. My system currently has 0 products, yet Square still has 2 live items.

I previously found and fixed a race on my side (an inventory.count.updated webhook echo triggered a second catalog create while the first was still in flight). I verified the fix with 29 consecutive create/delete cycles and zero duplicates. So either that regressed or there’s another path I haven’t found. I’m investigating locally, but I’d like to ask:

  • Can an upsert result in a new object ID rather than updating the existing object? I send an idempotency_key on create.
  • Does a large number of soft-deleted objects (search returns 1000+) affect anything? Can they be purged, or do they age out on their own?

I’m happy to provide catalog object IDs, the exact request payloads, and a full reproduction sequence.

Thanks.

Database Integration Next Steps

To feed orphaned IDs automatically from your SQLite database into this endpoint, you can query records where your local sync state shows a mismatch or where items are flagged as locally deleted but still present in Square.

import sqlite3

def get_local_orphaned_ids() -> list[str]:
    conn = sqlite3.connect("inventory.db")
    cursor = conn.cursor()
    # Example query retrieving IDs marked as deleted locally
    cursor.execute("SELECT square_catalog_id FROM products WHERE status = 'orphaned'")
    rows = cursor.fetchall()
    conn.close()
    return [row[0] for row in rows]

Let me know if you want to wire this up directly into a periodic FastAPI background worker (apscheduler or celery) so it runs automatically.

Thank you for your response. However, this does not address the issues I’m actually asking about.

I am not looking for a workaround to automatically process orphaned IDs. Before implementing any cleanup or background process, I need to understand why these orphaned IDs are being created in the first place.

Specifically:

  1. Why is Square generating a new Catalog Object ID for what should be the same item?
    Can an upsert operation create a new object ID instead of updating the existing object under any circumstances? If so, what conditions cause this?

  2. Why do items created through the Catalog API disappear from the Dashboard item library list after a page reload, while they remain searchable, visible in POS, and retrievable through ListCatalog and SearchCatalogObjects?
    What determines whether a Catalog Object is included in the Dashboard’s item library list?

These are not simply orphan-cleanup issues. They appear to indicate a problem with how the Catalog API and Dashboard are handling or indexing these objects.

I need to identify the root cause, not simply build a mechanism to clean up the symptoms afterward.

Could you please address these two questions directly?