Collections

Tenant and Catalog

Tenant, locations, staff, and products collections.

Tenant document

Path

tenants/{tenantId}

Operation

  • upsertTenant(data: Partial<Tenant>)

Creates or updates tenant metadata using merge semantics.

await client.upsertTenant({
  name: "Frappe Athens",
  country: "GR",
  timezone: "Europe/Athens",
})

Locations collection

Path

tenants/{tenantId}/locations/{locationId}

Operation

  • upsertLocation(locationId: string, data: Partial<Location>)

Use this for store-level metadata (address, currency, cutover time, etc).

await client.upsertLocation("loc_syntagma", {
  name: "Syntagma",
  address: "Mitropoleos 10",
  currency: "EUR",
  businessDayCutoverTime: "05:00",
})

Staff collection

Path

tenants/{tenantId}/staff/{uid}

Operation

  • upsertStaff(uid: string, data: Partial<StaffMember>)

Use this to store role assignments, status, and location visibility.

await client.upsertStaff("uid_manager_1", {
  roles: ["manager"],
  status: "active",
  locationIds: ["loc_syntagma"],
})

Products collection

Path

tenants/{tenantId}/products/{productId}

Operations

  • upsertProduct(productId: string, data: Partial<Product>)
  • subscribeProducts(listener, options?)

subscribeProducts supports query options (where, orderBy, limit) through QueryOptions.

await client.upsertProduct("espresso", {
  name: "Espresso",
  price: 2.5,
  taxRate: 0.24,
  isActive: true,
  categoryId: "coffee",
})

const stop = client.subscribeProducts((products) => {
  console.log("products:", products.length)
}, {
  where: [{ fieldPath: "isActive", op: "==", value: true }],
  orderBy: [{ fieldPath: "name", direction: "asc" }],
})

// later
stop()
Copyright © 2026