What this helps you do

The query and preview sit beside one compact controls card. When the source has a date column, the date button above the query opens its date settings; changes update the query, and Done closes the popup. The query editor toolbar includes Undo query edit, Redo query edit, Query help, and Collapse query editor. The editor starts collapsed. Select Expand query editor to open it. Collapsing the editor keeps your query and edit history. The keyboard shortcut hint explains how to run the query and open suggestions. Available table suggestions include descriptions; the full table and column reference is below.

Write a query report when you need a calculation or grouping that the visual report builder does not offer. A query is a way to ask the store's reporting data a precise question.

You can also start from a Freeform report. Its query box is directly editable, beside the same right-hand controls. The report keeps its header, dates, controls and results on the same screen. Select Run query after changing the query. Supported changes to fields, filters, dates, row limit or sorting update the matching source controls after the query runs. Until the edit has been checked, those source controls are unavailable so they cannot overwrite your draft.

Changing a source control updates the query. When changes cannot be combined with your edits, Upstorr asks which version to keep. A custom calculation the source controls cannot describe stays in the query; after running it, available query result controls let you choose its fields and chart in the same sidebar. Relative dates move forward each time the query runs; custom dates stay fixed until edited. The first query line records the linked report's Compare choice. Summary and comparison appear while the final query still matches the source settings; a matching daily line or area chart can also show the comparison line. A changed calculation that no longer matches those settings hides source-only totals and comparison. The final query result and chart remain available. Saving keeps the source settings and final query in one custom report.

Live online store sessions, page views, events, page loading, layout stability and responsiveness use the selected store's activity source in the same workspace. The database-backed query controls described below apply to database-backed reports.

For a simple chart with one group, the query copy keeps the chart style, fields, and chosen number of chart groups. A stacked vertical or horizontal bar chart with two groups keeps both groups, the chart style, and the chart group count. If the original chart has settings the query chart cannot reproduce, the query copy shows the same table rows and explains why the chart was not copied. Choose another chart in query if needed.

The page speed reports use totalPageLoads() in their live activity query to calculate each page path's or page type's share of page loads. It counts recorded page openings for the selected store, dates, and filters before the table's row limit. This function is available for page loading, layout stability, and responsiveness reports. Older measurements without a page type appear as Unclassified.

Theme activity marks stay in the visual report.

Before you start

  • Use Upstorr Admin on a desktop. Store admin pages do not open on mobile devices yet.

  • Your role needs permission to view reports.

  • To select Write query and start a database-backed report, query reporting must be available. If Write query is missing, ask Upstorr support whether reporting is configured and whether your role can view reports. To edit a live storefront activity report, open that visual report and select Edit query.

  • Check money amounts carefully. Orders may have different currencies. Group money totals by currency or convert them using the order's exchange rate before adding them together.

  • The available database views include orders, items, payments, returns, current inventory, inventory transfers, their items and recorded receiving history, products, variants, customers, stock adjustments, stock state changes, and historical session, page-view, and event records. The query copy of a database-backed visual report can use additional case-sensitive source view names such as "Order", "InventoryTransferItem", and "InventoryAdjustmentState"; keep their double quotes when editing the query. "InventoryAdjustmentState" has one row for each recorded change to Available, Unavailable, or Committed stock, so one adjustment can produce two rows. Current storefront activity is recorded separately. Open a live activity visual report and select Edit query to query its scoped activity source.

  • Payment reports can group by the recorded digital wallet or card brand. A refund uses its recorded instrument or the original payment belonging to that same order and store. Missing details stay blank. These reporting fields do not include card numbers, customer cardholder names, or UPI addresses.

  • The query source "OrderEvent" includes recorded Razorpay dispute updates. Its metadata contains the provider, dispute and payment IDs, the original signed notification's signedWebhookPhase, and providerEventAt when recorded. New notifications also keep signedWebhookReasonCode, signedWebhookAmount, and signedWebhookCurrency separately from any later status check. FRAUD is an early warning and RETRIEVAL is a request for information; neither proves a chargeback. Use the first recorded CHARGEBACK notification for the chargeback date and count each payment once. A later status check does not prove when the chargeback began. Older updates without this notification history cannot provide that date; keep it blank.

  • Gift card balances and transactions are available in both the visual builder and query reports. gift_cards shows current balances, while gift_card_transactions records dated amounts. Group gift card money by currency. An ADJUSTED transaction records the size of a change, not whether it added or removed balance.

  • Chargeback rate uses actual Razorpay chargebacks divided by successful Razorpay payments. For example, 2 chargebacks and 50 payments give 4%. Its total uses all payments, so a 40% week and a 0% week do not automatically mean 20% overall. Cash and terminal payments, failed attempts, and separate refunds are excluded; the original successful payment still counts after a refund. Paid booking damage claims are included.

  • Chargeback rate (fraud) uses only actual chargebacks with a bank reason code verified for the recorded card network. For example, 1 fraud chargeback and 50 successful payments give 2%. Chargeback amount (fraud) uses the amount in the first signed chargeback notification, in your store currency. Missing card networks, unknown reason codes, missing original dates or amounts, and conflicting first notifications stay blank. Early fraud warnings and requests for information are not confirmed fraud chargebacks. A foreign-currency amount without a verified store-currency value stays blank.

  • "RazorpayPaymentCapture" provides the capture date from a verified payment.captured notification. "OrderPayment"."referenceId" links an order payment to that provider payment. "Booking" and "BookingDamageClaim" provide the booking-payment link and recorded paid amount. Each source stays limited to your store. An older payment without its original capture notification cannot prove its capture date: affected date groups and the rate stay blank instead of using the day Upstorr processed it. A period with no successful payments also has a blank rate.

Steps

  1. Open Reports.

  2. Select Write query beside New report.

  3. Open Report details and enter a Name.

  4. Optionally enter a Description.

  5. Select Expand query editor, then enter a read-only query in the query editor. For example, the sample query counts orders by sales channel for the last 30 days. The editor highlights query and suggests available table and column names as you type. Press Ctrl+Space to open suggestions.

  6. Select Run query in the editor toolbar, or press Ctrl+Enter (⌘+Enter on Mac), to preview the result. If PostgreSQL points to a mistake in your query, the affected word is underlined and the error appears below the editor. Editing the query clears that mark.

  7. Check the rows and numbers in Result. When query still matches its visual source, known measures keep that source's names and money or percent formatting in the table.

  8. After the query runs, expandable Metrics, Dimensions, Filters, and Sort and limit sections appear in the controls card for its result without changing your query. To filter, set dates, or sort the returned rows, change a control. Your original query is then placed inside a new query that keeps its result columns. Select Add measure when you want to count or combine those rows into a summary; then you can group them. You can add up to 12 measures, 8 filters, and a row limit from 1 to 1,000. If your query returns a date or timestamp column, open the date button above the query and select Add date range. Choose Custom dates for fixed India calendar days, or choose Last 30 days, This month, or another moving range. Last number of units lets you choose a number of minutes, hours, days, weeks, months, quarters, or years and whether to include the current unit. Date-only columns cannot use minutes or hours. Moving ranges update each time the report runs; the end date of a custom range is included. For several matching values in one field, choose Is one of and enter one value per line. Is not one of excludes those values. Text fields also offer Contains, Does not contain, Starts with, and Ends with. These text rules ignore letter case and treat % and _ as ordinary characters. Is not, Is not one of, and Does not contain include rows where the field is blank. Filters on different fields all have to match. Count rows works for any result. Sum, Average, Minimum, and Maximum need a numeric result column.

  9. Select Run query again after changing a control. The controls write query immediately; the result and chart update only after you run it. If you edit the query yourself, run it before using the controls again. query that the controls cannot describe still runs as code. For money totals, group by currency or convert amounts in your source query before adding them.

    Add filter starts with an editable example value: zero for a number or today's India date when only a date field is available. Choose the field, rule, and value you actually want before running the query.

  10. Open Visualization and choose Display if you want a single value, vertical or horizontal bars, stacked bars, stacked area, line, area, pie, donut, or heatmap. For charts that offer it, set Chart groups from 1 to 1,000. This changes the chart only; the result table keeps its loaded rows.

  11. For a bar, line, area, pie, or donut chart, choose the Labels column. Stacked charts need two different label columns; stacked area uses dates for the first label and another field for its segments. A single value shows the first result row, so aggregate your query to one total when you use it.

  12. For a heatmap, choose one group column for Rows and a different group column for Columns. Your query must return one row for each pair of groups. For example, group orders by sales channel and payment status.

  13. For any chart, choose the Numbers column. A Measure list needs one row of totals. When opened from a dated visual report, it uses the matching source’s full-period totals instead of the first day. If the query changes beyond the visual source, the list asks you to run matching query rather than guessing a total.

    A missing number stays blank in a single value or measure list; it is not zero. Bar, line, area, pie, and donut charts leave out rows with a missing number and show a notice. The result table keeps those rows so you can see what is missing.

  14. Select Save to keep the query and chart in the store's report list. The button becomes available after a successful preview, a name, and valid chart columns.

The controls can narrow or summarize rows returned by your original query. They cannot restore rows excluded by a date rule, filter, or LIMIT inside that query. To include more rows, change the original query and run it again.

Tables and columns

Use the exact table and column names below in your query. A column is one piece of information, such as an order date or amount. Names shown in double quotes must keep those quotes. Queries read only the selected store. The editor also suggests these names while you type.

orders

Table name: orders.

Columns: order_id, order_number, created_at, paid_at, channel, payment_status, payment_method, subtotal, discount, tax, shipping, total, currency, exchange_rate, shipping_state, discount_code, customer_name, risk_level, supply_type, cancelled_at, shipped_at, delivered_at, shipping_method, tracking_recorded.

order_items

Table name: order_items.

Columns: item_id, order_id, product_id, variant_id, product_name, quantity, price, discount_amount, line_total, hsn_code, cgst, sgst, igst, cess, created_at, unit_cost_at_sale.

payments

Table name: payments.

Columns: payment_id, order_id, method, status, amount, commerce_amount, captured_at, created_at.

returns

Table name: returns.

Columns: return_id, order_id, status, reason, refund_amount, restocking_fee, created_at.

inventory

Table name: inventory.

Columns: inventory_id, product_id, variant_id, location_id, on_hand, available, committed, unavailable, updated_at.

products

Table name: products.

Columns: product_id, product_name, slug, status, sku, barcode, list_price, sale_price, recorded_cost, track_quantity, is_physical, category_id, vendor_id, created_at, updated_at.

variants

Table name: variants.

Columns: variant_id, product_id, product_name, variant_name, sku, barcode, price_override, sale_price_override, cost_override, track_quantity, position, created_at, updated_at, is_default_variant, inventory_cost_for_valuation.

customers

Table name: customers.

Columns: customer_id, name, created_at, last_order_at, lifetime_value, lifecycle_stage, rfm_group.

inventory_adjustments

Table name: inventory_adjustments.

Columns: adjustment_id, product_id, variant_id, location_id, quantity_delta, available_delta, unavailable_delta, reason, created_at.

gift_cards

Table name: gift_cards.

Columns: card_id, initial_value, recorded_balance, usable_balance, currency, is_active, expires_at, created_at.

gift_card_transactions

Table name: gift_card_transactions.

Columns: transaction_id, card_id, order_id, amount, type, currency, created_at.

sessions

Table name: sessions.

Columns: session_id, started_at, ended_at, device, browser, os, country, city, referrer, utm_source, utm_medium, utm_campaign, utm_content, utm_term, landing_page, duration_seconds, page_view_count, event_count, is_active.

page_views

Table name: page_views.

Columns: page_view_id, session_id, path, title, referrer, duration_seconds, exit_page, created_at.

events

Table name: events.

Columns: event_id, session_id, event_name, event_type, product_id, order_id, quantity, search_query, created_at.

Customer

Table name: "Customer".

Columns: "createdAt", "email", "id", "lifecycleStage", "lifetimeValue", "merchantId", "name", "rfmGroup", "storeId", "lastOrderAt".

CustomerMarketingPreference

Table name: "CustomerMarketingPreference".

Columns: "customerId", "emailOptIn", "merchantId", "storeId".

GiftCard

Table name: "GiftCard".

Columns: "balance", "currency", "expiresAt", "id", "initialValue", "isActive", "merchantId", "storeId".

GiftCardTransaction

Table name: "GiftCardTransaction".

Columns: "amount", "createdAt", "giftCardId", "id", "merchantId", "storeId", "type".

FulfillmentAnalyticsEvent

Table name: "FulfillmentAnalyticsEvent".

Columns: "carrier", "eventAt", "id", "merchantId", "orderCreatedAt", "orderId", "stage", "storeId", "trackingIncluded".

InventoryAdjustment

Table name: "InventoryAdjustment".

Columns: "createdAt", "id", "locationId", "merchantId", "productId", "quantityDelta", "reason", "storeId".

InventoryAdjustmentState

Table name: "InventoryAdjustmentState".

Columns: "adjustmentId", "createdAt", "createdByStaffId", "id", "inventoryState", "locationId", "merchantId", "productId", "quantityDelta", "reason", "referenceDocumentId", "referenceDocumentType", "storeId", "variantId".

InventoryDailyBalance

Table name: "InventoryDailyBalance".

Columns: "id", "merchantId", "storeId", "locationId", "productId", "variantId", "productTitle", "variantTitle", "sku", "tracked", "available", "onHand", "committed", "unavailable", "unitCost", "unitPrice", "currency", "balanceDate", "captureKind", "capturedAt".

InventorySaleAllocation

Table name: "InventorySaleAllocation".

Columns: "merchantId", "storeId", "orderId", "orderItemId", "tracked", "productId", "variantId", "evidence".

InventoryLevel

Table name: "InventoryLevel".

Columns: "available", "committed", "locationId", "merchantId", "onHand", "productId", "storeId", "unavailable", "variantId".

InventoryLocation

Table name: "InventoryLocation".

Columns: "id", "merchantId", "name", "storeId", "isActive".

InventoryTransfer

Table name: "InventoryTransfer".

Columns: "createdAt", "fromLocationId", "id", "merchantId", "referenceNumber", "status", "storeId", "toLocationId", "tags", "shippedAt", "firstReceivedAt".

InventoryTransferItem

Table name: "InventoryTransferItem".

Columns: "createdAt", "id", "merchantId", "productId", "quantity", "storeId", "transferId", "variantId", "receivedQuantity", "rejectedQuantity".

InventoryTransferReceipt

Table name: "InventoryTransferReceipt".

Columns: "id", "merchantId", "storeId", "transferId", "lineId", "acceptedQuantity", "rejectedQuantity", "receivedAt".

Order

Table name: "Order".

Columns: "billingCity", "billingCountry", "billingState", "cancelledAt", "channel", "couponCode", "createdAt", "customerEmail", "customerName", "deliveredAt", "discount", "id", "marketCurrency", "marketExchangeRate", "merchantId", "orderNumber", "paymentMethod", "paymentStatus", "posLocationId", "posStaffId", "riskLevel", "shippedAt", "shipping", "shippingCity", "shippingCountry", "shippingMethod", "shippingState", "storeId", "subtotal", "supplyType", "tax", "tipAmount", "total", "totalCess", "totalCgst", "totalIgst", "totalSgst", "trackingNumber", "userId", "isExchange", "securityDepositTotal", "giftCardShippingAmount", "referenceSnapshot".

OrderItem

Table name: "OrderItem".

Columns: "gstRate", "assessableValue", "attributedStaffId", "cessAmount", "cgstAmount", "createdAt", "discountAmount", "discountLines", "giftCardAmount", "hsnCode", "id", "igstAmount", "lineTotal", "merchantId", "metadata", "name", "orderId", "price", "productId", "quantity", "sgstAmount", "shippingAmount", "shippingTaxAmount", "storeId", "unitCostAtSale", "variantId", "variant".

LocalDeliveryJourney

Table name: "LocalDeliveryJourney".

Columns: "id", "merchantId", "storeId", "orderId", "returnId", "kind".

LocalDeliveryEvent

Table name: "LocalDeliveryEvent".

Columns: "merchantId", "storeId", "journeyId", "status", "occurredAt".

OrderEvent

Table name: "OrderEvent".

Columns: "createdAt", "eventType", "id", "merchantId", "metadata", "orderId", "referenceSnapshot", "storeId".

Purchased Shiprocket labels have "eventType" = 'SHIPMENT_LABEL_PURCHASED'. Their "referenceSnapshot" includes the waybill, forward direction, purchase status and five label details: purchase date, courier name, selected service, origin country and destination country. It excludes private booking tokens.

OrderPayment

Table name: "OrderPayment".

Columns: "referenceId", "amount", "commerceAmount", "capturedAt", "securityDepositAmount", "metadata", "createdAt", "id", "merchantId", "method", "orderId", "status", "storeId".

The built-in payment reports use "capturedAt" for completed payments and refunds, and "createdAt" for attempts that have not completed. Dates are grouped in India time. A ₹100 payment started at 11:59 p.m. on Monday and completed at midnight belongs to Tuesday. A refund uses its own recorded completion date. Using "createdAt" for every row would answer when the payment was started instead.

For Razorpay payments and refunds, a verified completion notification supplies the provider's completion time, even when Upstorr receives it later. Repeated notifications keep the earliest verified completion. An older record without this notification still shows when Upstorr recorded its completion; it does not prove the original Razorpay time. Running an old report again can therefore show a corrected date after that notification arrives.

RazorpayPaymentCapture

Table name: "RazorpayPaymentCapture".

Columns: "id", "merchantId", "observedAt", "paymentId", "providerCapturedAt", "providerPaymentCreatedAt", "storeId", "feeCurrency", "feeAmount", "feeTaxAmount", "isInternational".

Recorded INR fees use rupees. "feeAmount" already includes the GST in "feeTaxAmount": a ₹59 fee with ₹9 GST costs ₹59, not ₹68. Subtract the tax component only when you specifically need the fee before GST. Missing fee or tax details stay blank; a recorded zero is different from a missing amount. Fees in a currency other than INR stay blank until their currency can be established accurately. Repeat notifications can fill missing details without changing known amounts. Do not count the same "paymentId" more than once when combining capture notifications.

Booking

Table name: "Booking".

Columns: "id", "merchantId", "orderId", "storeId".

BookingDamageClaim

Table name: "BookingDamageClaim".

Columns: "bookingId", "createdAt", "id", "merchantId", "paidAmount", "providerPaymentId", "settledAt", "storeId".

RazorpayPaymentDispute

Table name: "RazorpayPaymentDispute".

Columns: "amount", "amountDeducted", "currency", "disputeId", "id", "merchantId", "orderId", "paymentId", "phase", "providerCreatedAt", "reasonCode", "reasonDescription", "status", "storeId".

Return

Table name: "Return".

Columns: "createdAt", "id", "merchantId", "orderId", "reason", "refundAmount", "restockingFee", "status", "storeId", "updatedAt".

PosLocation

Table name: "PosLocation".

Columns: "id", "merchantId", "name", "storeId", "kind", "inventoryLocationId".

PosStaff

Table name: "PosStaff".

Columns: "id", "merchantId", "name", "storeId".

Product

Table name: "Product".

Columns: "costPrice", "id", "merchantId", "name", "storeId", "isPhysical", "price", "sku", "vendorId", "categoryId".

Vendor

Table name: "Vendor".

Columns: "id", "merchantId", "storeId", "name".

Category

Table name: "Category".

Columns: "id", "merchantId", "storeId", "name".

ProductVariant

Table name: "ProductVariant".

Columns: "costPrice", "id", "merchantId", "name", "options", "storeId", "productId", "sku", "price", "trackQuantity", "createdAt".

ReturnLineItem

Table name: "ReturnLineItem".

Columns: "id", "merchantId", "orderItemId", "quantity", "reason", "returnId", "storeId".

Shipment

Table name: "Shipment".

Columns: "merchantId", "orderId", "status", "storeId", "waybillNumber", "id", "createdAt", "carrier", "deliveredAt", "fulfillmentOrderId".

ShipmentBillingEntry

Table name: "ShipmentBillingEntry".

Columns: "id", "merchantId", "storeId", "connectionId", "sourceVersion", "providerTransactionId", "shipmentId", "waybillNumber", "returnWaybillNumber", "action", "charge", "description", "debitAmount", "creditAmount", "providerCreatedAtRaw", "isConflicted", "firstObservedAt", "lastObservedAt".

These are recorded Shiprocket account charges linked to your store's Shiprocket shipments. Join "shipmentId" to "Shipment"."id". A matching local-delivery shipment does not receive a Shiprocket charge. Debits are money taken from the account; credits are money added back. The provider's charge name explains the entry. Freight, weight adjustments and return-to-origin charges are different entries; do not treat every debit as the cost of the original delivery.

A missing amount stays blank. A recorded zero means zero. If repeated provider entries disagree, "isConflicted" is true: the first known amount is retained, but the correct amount is uncertain. Do not treat it as a confirmed expense. "providerCreatedAtRaw" keeps the provider's original date text; Upstorr does not guess its timezone or split a charge into GST and other costs.

The first history scan must finish before charges appear. Changing the connected account's credentials starts a fresh history check; earlier versions do not appear alongside the new version or double the same transaction. During later refreshes, existing entries can gain missing details or be marked conflicted. These are recorded entries, not a frozen or guaranteed complete expense total. No matching entry does not prove that delivery was free.

ShipmentBillingSync

Table name: "ShipmentBillingSync".

Columns: "connectionId", "merchantId", "storeId", "sourceVersion", "completedFrom", "completedTo", "completedAt", "hasUnidentifiedRows", "scanFrom", "scanTo".

This source shows the last completed billing-history scan for the connected account. A non-blank "scanFrom" or "scanTo" means a refresh is unfinished; wait before claiming a complete original shipping cost. A blank "completedAt" means the first scan has not completed. "hasUnidentifiedRows" means at least one matching charge lacked a usable transaction ID and could not be safely recorded. Keep affected costs unknown. The completion date describes that scan; it does not mean later refreshes cannot update the recorded entries.

Purchased shipping labels

Shipping labels by order shows the base cost of forward Shiprocket labels purchased through Upstorr, grouped by purchase date, order, countries, courier and service. Shipping labels over time shows the label count, cost and average cost per label, with the previous period for comparison. Both use the same visual controls and query editor.

The purchase date comes from the provider's label receipt. Shipping carrier is the recorded courier name; Shipping service is the selected Surface or Express mode. Older bookings without a recorded purchase date cannot be placed into these dated reports. Upstorr does not substitute the order date or today's date.

Label cost uses the recorded Freight Charge for that waybill. It excludes later weight adjustments, COD charges, return-to-origin charges and other adjustments. Return labels, cancelled labels and reversed purchases are excluded. Rebooking keeps the earlier purchase receipt; cancelling one waybill removes only that purchase.

Costs stay blank when the billing-history scan is unfinished or the base charge is missing or conflicting. A recorded zero means zero. If any included label has an unknown cost, the affected total and average stay blank. An empty period in Shipping labels over time has a count and cost of zero, with a blank average. Shipping labels by order has no row when no purchase matches.

For example, two September labels cost ₹59 each and one August label costs ₹29. The total is 3 labels costing ₹147 and the average is ₹147 ÷ 3 = ₹49. The total uses every matching label even if the table displays only one row.

Market and original fulfillment

"Market" exposes the recorded market id, name, merchantId, and storeId. Join the order's marketId to this view; a missing market stays blank instead of being guessed from a delivery address.

"FulfillmentOrder" exposes id, merchantId, storeId, orderId, type, status, and a restricted referenceSnapshot containing only the fulfillment source, exchange ID, and return ID. It does not expose a pickup code or customer address. A shipment's fulfillmentOrderId connects its billed charges to the original fulfillment or an exchange replacement.

Exchange

Table name: "Exchange".

Columns: "createdAt", "differenceAmount", "id", "merchantId", "orderId", "status", "storeId", "updatedAt".

ExchangeLineItem

Table name: "ExchangeLineItem".

Columns: "exchangeId", "id", "merchantId", "newOrderItemId", "originalOrderItemId", "quantity", "storeId".

Live storefront query columns

Live activity query uses a different set of fields for each visual source. Its table is named activity; it cannot be joined to the database tables above. Keep the selected source when writing its query. The raw blob and double fields have source-specific meanings. They are not interchangeable between datasets.

Live sessions

Use FROM activity after opening this source in the visual editor and choosing Edit query.

Columns: session_id, started_at, device, browser, country, referrer, source, medium, campaign, landing_page, page_views, page_view_events, events, cart_events, checkout_events, purchase_events.

Live traffic

Use FROM activity after opening this source in the visual editor and choosing Edit query.

Columns: identity_id, identity_type, session_id, started_at, page_view_events, country, city, region, referrer, device, landing_page, visit_kind.

Live human sessions

Use FROM activity after opening this source in the visual editor and choosing Edit query.

Columns: session_id, started_at, visit_kind, cart_events, checkout_events, completed_checkout_events.

Live page views

Use FROM activity after opening this source in the visual editor and choosing Edit query.

Columns: timestamp, index1, blob1, blob2, blob3, blob4, blob5, blob6, blob7, blob8, blob9, blob10, blob11, blob12, blob13, blob14, blob15, blob16, blob17, blob18, blob19, blob20, double1, double2, double3, double4, _sample_interval.

Live events

Use FROM activity after opening this source in the visual editor and choosing Edit query.

Columns: timestamp, index1, blob1, blob2, blob3, blob4, blob5, blob6, blob7, blob8, blob9, blob10, blob11, blob12, blob13, blob14, blob15, blob16, blob17, blob18, blob19, blob20, double1, double2, double3, double4, _sample_interval.

Live web performance

Use FROM activity after opening this source in the visual editor and choosing Edit query.

Columns: timestamp, index1, blob1, blob2, blob3, blob4, blob5, blob6, blob7, double1, double2, double3, double4, double5, double6, double7, _sample_interval.

Live web layout

Use FROM activity after opening this source in the visual editor and choosing Edit query.

Columns: timestamp, index1, blob1, blob2, blob3, blob4, blob5, blob6, blob7, blob8, double8, _sample_interval.

Live web interactivity

Use FROM activity after opening this source in the visual editor and choosing Edit query.

Columns: timestamp, index1, blob1, blob2, blob3, blob4, blob5, blob6, blob7, blob8, double8, _sample_interval.

Working with stock, money, and delivery

Join inventory.variant_id to variants.variant_id and variants.product_id to products.product_id when you want current stock alongside product names or recorded costs. Use variants.inventory_cost_for_valuation to match the visual inventory value report: default variants use the product's recorded cost, while other variants use their own cost. A missing or negative cost appears blank. These are today's catalog values, not the cost saved when an item was sold.

The order_items view includes unit_cost_at_sale for original product profit calculations. It is blank for older items that did not save a cost when sold and for items without a recorded cost. Join order_items.order_id to orders.order_id to check payment status, cancelled_at, and currency. Prices can be in a checkout currency while product cost is in the store currency, so convert prices using the order's exchange_rate before subtracting cost. Later returns and refunds need separate handling; this field alone does not calculate profit after refunds.

For exchanged products, "ExchangeLineItem".quantity is the number of new replacement items. It is not the number of old items returned. The matching "ReturnLineItem" records the outgoing quantity when that return was saved. Some older exchanges have no matching returned-item record, so a report cannot safely fill in their outgoing quantity.

For fulfillment and delivery reports, orders.shipped_at is when an order was marked fulfilled, and orders.delivered_at is when delivery was recorded. shipping_method is the order's recorded method. tracking_recorded is true when the order has a tracking number or a non-cancelled, non-failed shipment with a waybill. For example, SELECT COUNT(*) FILTER (WHERE shipped_at IS NOT NULL) AS fulfilled_orders, COUNT(*) FILTER (WHERE delivered_at IS NOT NULL) AS delivered_orders FROM orders counts each recorded stage. A blank date means that stage has not been recorded; these fields do not tell you when a carrier first handled the parcel.

Profit before returns

Profit margin by order and Average profit margin by market use the same editor and calculations. The market report includes fulfilled original orders and groups them by the recorded market.

Revenue includes original product sales after discounts, customer shipping charges, and recorded additional charges. Sales GST is shown separately and is excluded from revenue. Store costs include product cost recorded at sale, billed original shipping charges and weight adjustments, and recorded payment processing costs. Razorpay's recorded total fee already includes its GST and any international payment component; it is counted once. Marketing and packaging expenses are not included.

For example, ₹1,000 of product sales plus ₹100 of customer shipping gives ₹1,100 revenue. Product cost of ₹400, billed shipping of ₹70, and a recorded payment fee of ₹59 give ₹529 store costs and ₹571 profit before returns. Sales GST collected is separate. A later refund, return-only shipping charge, or exchange replacement does not change these original-sale numbers.

A missing or conflicting cost stays blank. It is not treated as zero. Unfinished shipping statement scans, unrecognized charge labels, and unrecorded delivery duties on international shipments also leave the affected cost and profit blank. Pickup or carry-out with recorded fulfillment evidence can have zero transport cost; a missing shipment alone does not prove free shipping. Local delivery quotes are not treated as final billed costs.

The Summary averages all included orders, even when a row limit hides some rows. It does not average the market averages. For example, a market containing two orders with profits of ₹571 and ₹1,200 averages ₹885.50. Another market with one ₹2,400 order averages ₹2,400. The Summary for all three orders is ₹1,390.33.

What happens next

On Reports, select the filter icon, add Category, and choose Custom to reopen a saved query report. Its result loads automatically. Change the query and select Run query again before Save. More → Save as new creates another report under a different name. The result preview shows up to 200 rows. The chart shows up to the selected group count, which defaults to 30; for a date label it shows the latest loaded dates in order. Existing saved query reports continue to use 30 until you choose another number. Export downloads the loaded rows as CSV, JSONL, XML, or Apache Parquet. When more rows match, Export also offers all matching rows in those formats, up to 100,000 rows. Include ORDER BY on a unique result key in your query so pages keep a stable order. The full export runs the query again for each page, so data changed during download can change the result. If more than 100,000 rows match, no partial file is downloaded; narrow the query and try again.

More → Delete report asks for confirmation and removes only the saved report. It does not delete orders, customers, or stock. To delete several saved reports together, select their checkboxes on Reports, select Delete, and confirm. Ready-made reports cannot be selected for deletion.

Common problems

  • query reports are not configured yet: ask Upstorr support to enable the reporting database.
  • This query refers to data outside the reporting area: use the reporting tables listed in this guide.
  • The query took too long: choose a shorter date range or simplify the query.
  • A word in the query is underlined: read the error below the editor, correct the underlined word, and run the query again. Some errors have no exact word to underline; the message still appears below the editor.
  • The query changed: run it again before saving or exporting the new result.
  • A linked report runs but no longer shows its visual totals: if you want to use the current visual settings instead of the saved query, select Refresh query from visual settings and confirm. This replaces any query-only edits in the draft. Check the new result, then select Save; the saved report does not change until you save it.
  • Save is unavailable after changing the query columns: the selected chart may still refer to columns from the old result. Choose the new label and number columns under Chart, or choose Table only.
  • The date range is invalid: choose complete start and end dates, with the start on or before the end. Run and Save stay unavailable until you correct it. Timestamp columns without a timezone are treated as UTC, as Upstorr stores them.
  • Controls show “Run the edited query to refresh the controls”: run the query. Supported controls then appear for its result. A very long query may leave too little room for the extra query that controls need; shorten the source query before using them.
  • The report query is too long: the editor accepts up to 65,536 characters, including query added by the visual controls. Shorten the query or remove an unnecessary control, then run it again.
  • No rows matched this query: check the conditions and dates. A table name that is not available shows an error instead.
  • The heatmap has more than one result row for a group pair: add GROUP BY for both group columns so each colored cell has one number.

Razorpay bank payouts

Connect your Razorpay account in Payment settings before using this report.

Payouts over time shows confirmed INR payouts from the Razorpay account currently connected to your store, on the date Razorpay settled each payout. These are the account's bank payouts, which can include payments collected outside Upstorr. They are separate from customer payment totals.

For example, a bank payout of ₹1,234.56 covering ten payments contributes ₹1,234.56 once. Upstorr does not add those ten payment amounts again or subtract provider fees from the already settled amount. Dates follow India time. A payout at 11:59 p.m. on 30 September belongs to September; one at midnight belongs to October.

Pending and failed transfers are not counted as confirmed bank payouts. Months with pending transfers are checked again so a later confirmation can appear.

Payout history syncs in the background. Until the history scan finishes, the report asks you to wait and run it again. A failed or unfinished scan does not become a zero payout. During a refresh, the last complete month remains visible until its replacement is complete. Changing the connected account hides the old account's payouts until the new history is ready.

The starting report includes the previous 89 days and today, giving 90 calendar days, with the preceding period for comparison. Use the shared report controls or edit the query, then choose Run query. Save the report to reopen it later.

Confirmed payout records

Table name: "PayoutReport".

Columns: id, merchantId, storeId, settlementId, amount, currency, settledAt, observedAt.

amount is the INR bank payout; settledAt is its settlement date. One settlement ID contributes once. No account credentials or bank reference numbers are exposed.

Payout history coverage

Table name: "PayoutReportCoverage".

Columns: id, merchantId, storeId, year, month, completedAt.

Each row confirms that a month's history has finished syncing. A completed month can have no payouts. A missing row means the month is not confirmed yet. Both tables are restricted to your current store.

Dated exchange rates

Admin reports show money in INR. A generated report_currency setting must remain WITH report_currency AS (SELECT 'INR'::text AS code). Foreign display-currency choices are not supported in Admin. For an older visual report using another currency, select Use INR for this report, run it and save it. Confirmed money columns in a linked report's downloaded file include the INR code. Custom formulas that no longer match the visual report keep their original query column names.

Table name: "ReportExchangeRate".

Columns: rateDate, baseCurrency, rates, source, publishedAt, finalized.

These are public currency rates, shared across stores. Each row is a recorded provider date in UTC, rather than today's rate copied into an older date. Only recorded dates are returned. A missing date or currency is unknown; do not replace it with zero or a current rate.

rates gives each currency's value against one USD. These source records can help explain recorded storefront conversion. Admin report money remains INR. Convert original checkout amounts with their recorded order exchange rate before combining them with INR amounts.

finalized is false for an unfinished current date. Its published rate can change until that day is complete. The following historical import confirms the completed day. A query using these rates changes report results only; it does not change order prices, payments, GST invoices, or checkout currency.