Skip to content

Placing orders

A Hub order goes: read the catalogue → build a cart of item ids and quantities → POST the order → track status until it settles. Prices never travel with the cart — Hub derives them from the synced catalogue on the server.

  1. Browse the community catalogue — member stores, categories and items with modifier groups, as mirrored from each merchant. Use store_id to fetch a single store’s slice and updated_since for incremental refreshes:

    Terminal window
    curl -s "https://hub-api.quiqqy.ai/api/v2/communities/com_4hx7d2/catalogue?store_id=st_8fk2c1p9" \
    -H "Authorization: Bearer $ACCESS_TOKEN"

    Item detail lives at /communities/{communityId}/catalogue/items/{itemId}. Cache catalogue reads briefly and honour menu-management availability — an unavailable item in the cart fails placement.

  2. A cart is a list of lines, each naming the catalogue item, a quantity and any selected modifiers (plus the fulfilling store, recommended for explicitness). Lines may span several stores; Hub will split them into one child order per store.

  3. Terminal window
    curl -s "https://hub-api.quiqqy.ai/api/v2/communities/com_4hx7d2/orders" \
    -H "Authorization: Bearer $ACCESS_TOKEN" \
    -H 'Content-Type: application/json' \
    -H 'Idempotency-Key: 9f2c1e8a-7b6d-4c5e-8f9a-0b1c2d3e4f5a' \
    -d '{
    "fulfillment_type": "delivery",
    "customer": {
    "name": "Anita R.",
    "email": "anita@example.com",
    "phone": "+919812345678"
    },
    "delivery_address": {
    "street_address": "14 Marine Drive, Apt 6C",
    "city": "Kochi",
    "state": "KL",
    "country": "IN",
    "zip": "682031"
    },
    "items": [
    {
    "item_id": "itm_9f8e7d6c5b4a",
    "store_id": "st_8fk2c1p9",
    "quantity": 2,
    "modifiers": [{ "modifier_id": "mod_3c4d5e6f7a8b", "quantity": 1 }]
    },
    {
    "item_id": "itm_5c4b3a2d1e0f",
    "store_id": "st_5mq3n7v1",
    "quantity": 2
    }
    ],
    "tip": 200,
    "special_instructions": "Gate code 4471",
    "payment": { "method": "card", "provider": "stripe" }
    }'

    Field notes:

    FieldNotes
    fulfillment_typedelivery, pickup or dine_in — required
    customerRequired — name plus email/phone (Quiqqy finds-or-creates the community customer), or a known customer_id
    items[].item_idCatalogue item id; the line’s price comes from the catalogue, never from the request
    items[].store_idThe merchant store fulfilling that line — optional (inferred from the item) but recommended
    items[].modifiers[]Selected modifiers by modifier_id, with quantity
    payment.methodcard, digital_wallet, cash, bank_transfer — required; payment.provider names the collector, e.g. stripe
    delivery_addressRequired for delivery orders
    tip, expected_totalInteger minor units
    coupon_code, special_instructions, pickup_atOptional
    Idempotency-Key headerMakes retries safe — the same key returns the original order instead of creating a duplicate
  4. Read the order back at GET /communities/{communityId}/orders/{orderId} (or list with GET /communities/{communityId}/orders, filterable by status, store and date range), or skip polling entirely and subscribe to order.status_changed.

pending → confirmed → preparing → ready → out_for_delivery → delivered
└──── (pickup ends at ready)
any non-terminal state → cancelled
StatusMeaning
pendingPlaced; child orders forwarded to each store
confirmedStore(s) accepted
preparingKitchen working
readyReady for pickup / courier handoff
out_for_deliveryCourier en route (delivery orders)
deliveredTerminal success
cancelledTerminal — cancelled by shopper, store or operator

Payment status is tracked separately: pending, processing, completed, failed, refunded.

POST /communities/{communityId}/orders/{orderId}/cancel cancels the parent order and every child that has not yet reached a terminal state. Send a customer-facing reason (free text) and, optionally, a structured reason_code:

{ "reason": "Customer requested cancellation", "reason_code": "OTHER" }

Cancellation is only possible while no child is out_for_delivery or later — after that the call returns 409 order.not_cancellable. Paid orders move to payment_status: refunded once the refund settles. Details in the Hub Ordering reference.