> ## Documentation Index
> Fetch the complete documentation index at: https://twigz.arqqin.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Store Onboarding

> Administrative store creation, product management, and onboarding operations

## Overview

Store Onboarding API provides administrative functions for creating and managing stores, products, store categories, and delivery zones on behalf of store owners. These are admin-only mutations used during the onboarding flow or for ongoing store management.

**Location:** `convex/admins/storeOnboarding.ts`

***

## Admin Create Store

Creates a new store with optional auto-approval. When auto-approved, the store is immediately set to active and a wallet is initialized. Increments the `storeCount` on the assigned primary category.

<ParamField path="name" type="string" required>
  Store name
</ParamField>

<ParamField path="nameArabic" type="string">
  Store name in Arabic
</ParamField>

<ParamField path="description" type="string">
  Store description
</ParamField>

<ParamField path="primaryCategory" type="Id<'categories'>" required>
  Primary category ID for the store
</ParamField>

<ParamField path="ownerUserId" type="string">
  Owner's Clerk user ID. Defaults to `"admin_managed"` if not provided.
</ParamField>

<ParamField path="ownerName" type="string" required>
  Store owner's full name
</ParamField>

<ParamField path="phoneNumber" type="string" required>
  Store owner's phone number
</ParamField>

<ParamField path="email" type="string">
  Store contact email
</ParamField>

<ParamField path="logoId" type="Id<'_storage'>">
  Storage ID for the store logo image
</ParamField>

<ParamField path="coverImageId" type="Id<'_storage'>">
  Storage ID for the store cover/banner image
</ParamField>

<ParamField path="tradeLicenseId" type="Id<'_storage'>">
  Storage ID for the trade license document
</ParamField>

<ParamField path="autoApprove" type="boolean">
  If true, store is created with `approved` status, `isActive: true`, and a wallet is initialized automatically
</ParamField>

<ParamField path="settings" type="object">
  Store settings:

  * `deliveryEnabled` — Whether delivery is enabled
  * `pickupEnabled` — Whether pickup is enabled
  * `allowsReturns` — Whether returns are accepted
  * `returnWindowDays` — Return window in days
</ParamField>

<ParamField path="bankAccount" type="object">
  Bank account details:

  * `IBAN` — Bank IBAN number
  * `bankName` — Bank name
  * `bankId` — Bank ID reference
  * `accountHolderName` — Account holder name
</ParamField>

<ParamField path="address" type="object">
  Store address:

  * `fullAddress` — Full address string
  * `city` — City ID
  * `area` — Area ID
  * `flatVilaNumber` — Flat/villa number
  * `buildingNameNumber` — Building name or number
  * `landmark` — Nearby landmark
  * `latitude` — Latitude coordinate
  * `longitude` — Longitude coordinate
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const storeId = await convex.mutation(api.admins.storeOnboarding.adminCreateStore, {
    name: "Pizza Palace",
    nameArabic: "قصر البيتزا",
    description: "Best pizza in town",
    primaryCategory: "cat_food",
    ownerName: "Ahmed Ali",
    phoneNumber: "+971501234567",
    email: "ahmed@pizzapalace.ae",
    autoApprove: true,
    settings: {
      deliveryEnabled: true,
      pickupEnabled: true,
      allowsReturns: false
    },
    bankAccount: {
      IBAN: "AE070331234567890123456",
      bankName: "Emirates NBD",
      accountHolderName: "Ahmed Ali"
    },
    address: {
      fullAddress: "Building 5, Downtown Dubai",
      city: "city_dubai",
      area: "area_downtown"
    }
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  "s123456789"
  ```
</ResponseExample>

***

## Admin Update Store

Updates an existing store's fields. Only provided fields are updated. If `primaryCategory` changes, the denormalized `storeCount` on both old and new categories is updated.

<ParamField path="storeId" type="Id<'stores'>" required>
  Store ID to update
</ParamField>

<ParamField path="name" type="string">
  Updated store name
</ParamField>

<ParamField path="nameArabic" type="string">
  Updated store name in Arabic
</ParamField>

<ParamField path="description" type="string">
  Updated description
</ParamField>

<ParamField path="primaryCategory" type="Id<'categories'>">
  Updated primary category (triggers counter updates)
</ParamField>

<ParamField path="ownerUserId" type="string">
  Updated owner user ID
</ParamField>

<ParamField path="ownerName" type="string">
  Updated owner name
</ParamField>

<ParamField path="phoneNumber" type="string">
  Updated phone number
</ParamField>

<ParamField path="email" type="string">
  Updated email
</ParamField>

<ParamField path="logoId" type="Id<'_storage'>">
  Updated logo storage ID
</ParamField>

<ParamField path="coverImageId" type="Id<'_storage'>">
  Updated cover image storage ID
</ParamField>

<ParamField path="tradeLicenseId" type="Id<'_storage'>">
  Updated trade license storage ID
</ParamField>

<ParamField path="settings" type="object">
  Updated store settings (same structure as create)
</ParamField>

<ParamField path="bankAccount" type="object">
  Updated bank account details (same structure as create)
</ParamField>

<ParamField path="address" type="object">
  Updated store address (same structure as create)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const storeId = await convex.mutation(api.admins.storeOnboarding.adminUpdateStore, {
    storeId: "s123456789",
    name: "Pizza Palace Premium",
    description: "Premium pizza experience",
    settings: {
      deliveryEnabled: true,
      pickupEnabled: true,
      allowsReturns: true,
      returnWindowDays: 7
    }
  });
  ```
</CodeGroup>

***

## Admin Create Product

Creates a product for a store. Validates that the product category is a child of the store's primary category.

<ParamField path="storeId" type="Id<'stores'>" required>
  Store ID to add the product to
</ParamField>

<ParamField path="name" type="string" required>
  Product name
</ParamField>

<ParamField path="description" type="string">
  Product description
</ParamField>

<ParamField path="category" type="Id<'categories'>" required>
  Product category ID (must be a child of the store's primary category)
</ParamField>

<ParamField path="price" type="number" required>
  Product price in AED
</ParamField>

<ParamField path="cost" type="number">
  Product cost (for profit tracking)
</ParamField>

<ParamField path="stock" type="number">
  Available stock quantity
</ParamField>

<ParamField path="stockAlert" type="number">
  Low stock alert threshold
</ParamField>

<ParamField path="prepTime" type="number">
  Preparation time value
</ParamField>

<ParamField path="prepTimeUnit" type="string">
  Preparation time unit: `minutes`, `hours`, or `days`
</ParamField>

<ParamField path="isActive" type="boolean" required>
  Whether the product is active and visible
</ParamField>

<ParamField path="primaryImage" type="Id<'_storage'>" required>
  Storage ID for the primary product image
</ParamField>

<ParamField path="images" type="Id<'_storage'>[]">
  Additional product image storage IDs
</ParamField>

<ParamField path="isReturnable" type="boolean">
  Whether the product can be returned
</ParamField>

<ParamField path="isDeliveryEnabled" type="boolean">
  Whether delivery is available for this product
</ParamField>

<ParamField path="isPickupEnabled" type="boolean">
  Whether pickup is available for this product
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const productId = await convex.mutation(api.admins.storeOnboarding.adminCreateProduct, {
    storeId: "s123456789",
    name: "Margherita Pizza",
    description: "Classic margherita with fresh mozzarella",
    category: "cat_pizza",
    price: 45.00,
    cost: 15.00,
    stock: 100,
    stockAlert: 10,
    prepTime: 20,
    prepTimeUnit: "minutes",
    isActive: true,
    primaryImage: "img_storage_id",
    isDeliveryEnabled: true,
    isPickupEnabled: true
  });
  ```
</CodeGroup>

***

## Admin Update Product

Updates an existing product. Only provided fields are modified.

<ParamField path="productId" type="Id<'products'>" required>
  Product ID to update
</ParamField>

<ParamField path="name" type="string">
  Updated product name
</ParamField>

<ParamField path="description" type="string">
  Updated description
</ParamField>

<ParamField path="category" type="Id<'categories'>">
  Updated category
</ParamField>

<ParamField path="price" type="number">
  Updated price
</ParamField>

<ParamField path="cost" type="number">
  Updated cost
</ParamField>

<ParamField path="stock" type="number">
  Updated stock quantity
</ParamField>

<ParamField path="stockAlert" type="number">
  Updated stock alert threshold
</ParamField>

<ParamField path="prepTime" type="number">
  Updated preparation time
</ParamField>

<ParamField path="prepTimeUnit" type="string">
  Updated preparation time unit: `minutes`, `hours`, or `days`
</ParamField>

<ParamField path="isActive" type="boolean">
  Updated active status
</ParamField>

<ParamField path="primaryImage" type="Id<'_storage'>">
  Updated primary image storage ID
</ParamField>

<ParamField path="images" type="Id<'_storage'>[]">
  Updated additional images
</ParamField>

<ParamField path="isReturnable" type="boolean">
  Updated returnable flag
</ParamField>

<ParamField path="isDeliveryEnabled" type="boolean">
  Updated delivery flag
</ParamField>

<ParamField path="isPickupEnabled" type="boolean">
  Updated pickup flag
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const productId = await convex.mutation(api.admins.storeOnboarding.adminUpdateProduct, {
    productId: "prod_123456789",
    price: 50.00,
    stock: 200,
    isActive: true
  });
  ```
</CodeGroup>

***

## Admin Delete Product

Permanently deletes a product from the database.

<ParamField path="productId" type="Id<'products'>" required>
  Product ID to delete
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await convex.mutation(api.admins.storeOnboarding.adminDeleteProduct, {
    productId: "prod_123456789"
  });
  // result: { success: true }
  ```
</CodeGroup>

***

## Admin Create Store Category

Creates a store-level category under the store's primary category. Store categories are used to organize products within a store.

<ParamField path="storeId" type="Id<'stores'>" required>
  Store ID to create the category for
</ParamField>

<ParamField path="name" type="string" required>
  Category name in English
</ParamField>

<ParamField path="nameArabic" type="string" required>
  Category name in Arabic
</ParamField>

<ParamField path="isActive" type="boolean">
  Whether the category is active (default: true)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const categoryId = await convex.mutation(api.admins.storeOnboarding.adminCreateStoreCategory, {
    storeId: "s123456789",
    name: "Pizzas",
    nameArabic: "بيتزا",
    isActive: true
  });
  ```
</CodeGroup>

***

## Admin Update Store Category

Updates a store-level category. Cannot update global (platform-level) categories.

<ParamField path="categoryId" type="Id<'categories'>" required>
  Category ID to update
</ParamField>

<ParamField path="name" type="string">
  Updated category name
</ParamField>

<ParamField path="nameArabic" type="string">
  Updated Arabic category name
</ParamField>

<ParamField path="isActive" type="boolean">
  Updated active status
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const categoryId = await convex.mutation(api.admins.storeOnboarding.adminUpdateStoreCategory, {
    categoryId: "cat_123456789",
    name: "Specialty Pizzas",
    nameArabic: "بيتزا مميزة"
  });
  ```
</CodeGroup>

<Note>
  This mutation only works on store-level categories (those with a `storeId` field). Attempting to update a global category will throw an error.
</Note>

***

## Admin Delete Store Category

Permanently deletes a store-level category. Cannot delete global categories.

<ParamField path="categoryId" type="Id<'categories'>" required>
  Category ID to delete
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await convex.mutation(api.admins.storeOnboarding.adminDeleteStoreCategory, {
    categoryId: "cat_123456789"
  });
  // result: { success: true }
  ```
</CodeGroup>

<Note>
  This mutation only works on store-level categories. Products assigned to this category will retain their category reference, but the category will no longer exist.
</Note>

***

## Admin Create Delivery Zone

Creates or updates a delivery zone for a store. If a zone already exists for the same store and city combination, it is updated (upsert behavior).

<ParamField path="storeId" type="Id<'stores'>" required>
  Store ID to create the delivery zone for
</ParamField>

<ParamField path="city" type="Id<'cities'>" required>
  City ID for the delivery zone
</ParamField>

<ParamField path="areas" type="array" required>
  Array of area configurations, each containing:

  * `area` (Id\<'areas'>, required) — Area ID
  * `deliveryFee` (number) — Delivery fee for this area in AED
  * `deliveryTime` (number) — Estimated delivery time
  * `deliveryTimeUnit` (string) — Time unit: `minutes`, `hours`, or `days`
</ParamField>

<ParamField path="deliveryFee" type="number">
  Default delivery fee for the zone in AED
</ParamField>

<ParamField path="deliveryTime" type="number">
  Default delivery time for the zone
</ParamField>

<ParamField path="deliveryTimeUnit" type="string">
  Default delivery time unit: `minutes`, `hours`, or `days`
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await convex.mutation(api.admins.storeOnboarding.adminCreateDeliveryZone, {
    storeId: "s123456789",
    city: "city_dubai",
    areas: [
      { area: "area_downtown", deliveryFee: 10, deliveryTime: 30, deliveryTimeUnit: "minutes" },
      { area: "area_marina", deliveryFee: 15, deliveryTime: 45, deliveryTimeUnit: "minutes" }
    ],
    deliveryFee: 12,
    deliveryTime: 35,
    deliveryTimeUnit: "minutes"
  });
  // result: { id: "dz123456789", action: "created" }
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "id": "dz123456789",
    "action": "created"
  }
  ```
</ResponseExample>

<Note>
  If a delivery zone for the same store + city combination already exists, the zone is updated instead of creating a duplicate. The response `action` field indicates whether the zone was `"created"` or `"updated"`.
</Note>

***

## Admin Delete Delivery Zone

Permanently deletes a delivery zone.

<ParamField path="zoneId" type="Id<'storeDeliveryZones'>" required>
  Delivery zone ID to delete
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await convex.mutation(api.admins.storeOnboarding.adminDeleteDeliveryZone, {
    zoneId: "dz123456789"
  });
  // result: { success: true }
  ```
</CodeGroup>
