← Back to Metrics

Curbside Orders

Also known as: Curbside Pickup Orders, Drive-Up Orders, Kerbside Collection Orders, Curbside Order Count

OperationsmetricRetail: Omnichannel (BOPIS - Buy Online Pick Up In Store)Retail

Online orders placed with curbside pickup as the fulfillment method, one per transaction_id by order date; a subset of BOPIS Orders. Numerator of Curbside Pickup Rate; counts orders placed, not curbside check-ins.

Business Use Case

1️⃣ Store operations staffs curbside bays and runners per store and hour from last week's curbside order count.,2️⃣ Sales reviews curbside orders in stores newly enabled for curbside and widens or pauses the rollout.,3️⃣ Marketing tracks curbside orders during a pickup promotion and ends the offer if the count stays flat.

Business Criticality

Medium

Journey / Navigation Area

Checkout

Owning Function

Sales

Scope

Event

Formula

COUNT(DISTINCT transaction_id) on purchase events WHERE fulfillment_method = 'curbside', by order date in the reporting period

Calculation Notes

1️⃣ Distinct transaction_id on purchase events with fulfillment_method = 'curbside'; repeat purchase hits for one order count once.,2️⃣ Counted on the order date, not the pickup date; the method selected at checkout holds even if the customer later switches pickup type.,3️⃣ Subset of BOPIS Orders: in-store counter, locker and ship-to-store pickups are excluded and stay in their own counts.,4️⃣ Map platform values (curbside_pickup, drive_up, kerbside) to curbside through the fulfillment-method dimension before counting.,5️⃣ Tagging rule: the purchase hit sends order-level fulfillment_method 'curbside' when any line is curbside; split orders count once.,6️⃣ Internal and test orders (traffic_type = internal) are excluded; cancelled orders stay in, since the metric counts orders placed.,7️⃣ Not the curbside-pickup-check-in event: arrivals and handovers are measured by that event and Pickups Completed, after the order.

Technical Details

Measure TypeCounter
Aggregation WindowDaily, Weekly, Monthly

Events

PlatformEvent
Google Analytics 4purchase
Adobepurchase

Data Mappings

W3 Data Layer
{
  "transaction": {
    "transactionID": "txn-1",
    "total": {
      "basePrice": 100,
      "currency": "USD"
    },
    "item": [
      {
        "productInfo": {
          "sku": "SKU-1",
          "productName": "item"
        },
        "quantity": 1,
        "price": {
          "basePrice": 100
        }
      }
    ],
    "attributes": {
      "fulfillmentMethod": "curbside",
      "trafficType": "external"
    }
  }
}
GA4 Data Layer
{
  "event": "purchase",
  "fulfillment_method": "curbside",
  "traffic_type": "external",
  "ecommerce": {
    "transaction_id": "txn-1",
    "value": 100,
    "currency": "USD",
    "items": [
      {
        "item_id": "SKU-1",
        "item_name": "item",
        "price": 100,
        "quantity": 1
      }
    ]
  }
}
Adobe Client Data Layer
{
  "event": "purchase",
  "eventInfo": {
    "fulfillmentMethod": "curbside",
    "trafficType": "external"
  },
  "xdm": {
    "_tenantId": {
      "fulfillmentMethod": "curbside",
      "trafficType": "external"
    },
    "commerce": {
      "purchases": {
        "value": 1
      },
      "order": {
        "purchaseID": "txn-1",
        "currencyCode": "USD",
        "priceTotal": 100
      }
    },
    "productListItems": [
      {
        "SKU": "SKU-1",
        "name": "item",
        "quantity": 1,
        "priceTotal": 100
      }
    ]
  }
}
XDM Mapping
{
  "eventType": "commerce.purchases",
  "commerce": {
    "purchases": {
      "value": 1
    },
    "order": {
      "purchaseID": "txn-1",
      "currencyCode": "USD",
      "priceTotal": 100
    }
  },
  "productListItems": [
    {
      "SKU": "SKU-1",
      "quantity": 1,
      "priceTotal": 100
    }
  ],
  "_tenantId": {
    "fulfillmentMethod": "curbside",
    "trafficType": "external"
  }
}

SQL Queries

Google Analytics 4 (BigQuery)
-- ==========================================
-- 1. GOOGLE BIGQUERY SQL
-- ==========================================
DECLARE start_date DATE DEFAULT DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY);
DECLARE end_date DATE DEFAULT DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY);

-- Purchase hits in the window with event-level fulfillment_method and traffic_type pivoted from event_params
WITH purchase_hits AS (
  SELECT
    ecommerce.transaction_id AS transaction_id,
    PARSE_DATE('%Y%m%d', event_date) AS event_date,
    LOWER((SELECT ep.value.string_value FROM UNNEST(event_params) AS ep WHERE ep.key = 'fulfillment_method')) AS fulfillment_method,
    (SELECT ep.value.string_value FROM UNNEST(event_params) AS ep WHERE ep.key = 'traffic_type') AS traffic_type
  FROM `your-project.analytics_123456789.events_*`
  WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', start_date) AND FORMAT_DATE('%Y%m%d', end_date)
    AND event_name = 'purchase'
    AND ecommerce.transaction_id IS NOT NULL
),
-- One row per order: first purchase date as order date; curbside if any hit maps to a curbside value (split orders count once); internal if any hit is internal
orders AS (
  SELECT
    transaction_id,
    MIN(event_date) AS order_date,
    LOGICAL_OR(fulfillment_method IN ('curbside', 'curbside_pickup', 'drive_up', 'kerbside')) AS is_curbside,
    LOGICAL_OR(traffic_type = 'internal') AS is_internal
  FROM purchase_hits
  GROUP BY transaction_id
)
-- Distinct curbside orders by order date, internal and test orders excluded, cancelled orders kept
SELECT
  order_date,
  COUNT(DISTINCT transaction_id) AS curbside_orders
FROM orders
WHERE is_curbside
  AND NOT COALESCE(is_internal, FALSE)
GROUP BY order_date
ORDER BY order_date;
Adobe Experience Platform (AEP Query Service)
-- ==========================================
-- 2. ADOBE AEP QUERY SERVICE SQL
-- ==========================================
-- Reporting window: last 90 complete days
WITH params AS (
  SELECT date_sub(current_date(), 90) AS start_date, date_sub(current_date(), 1) AS end_date
),
-- Purchase hits in the window with the tenant fulfillmentMethod and trafficType (tenant counterpart of GA4 traffic_type)
purchase_hits AS (
  SELECT
    e.commerce.order.purchaseID AS transaction_id,
    to_date(e.timestamp) AS event_date,
    lower(e._tenantId.fulfillmentMethod) AS fulfillment_method,
    e._tenantId.trafficType AS traffic_type
  FROM aep_web_events_dataset AS e
  CROSS JOIN params AS p
  WHERE e.eventType = 'commerce.purchases'
    AND to_date(e.timestamp) BETWEEN p.start_date AND p.end_date
    AND e.commerce.order.purchaseID IS NOT NULL
),
-- One row per order: first purchase date as order date; curbside if any hit maps to a curbside value (split orders count once); internal if any hit is internal
orders AS (
  SELECT
    transaction_id,
    MIN(event_date) AS order_date,
    MAX(CASE WHEN fulfillment_method IN ('curbside', 'curbside_pickup', 'drive_up', 'kerbside') THEN 1 ELSE 0 END) AS is_curbside,
    MAX(CASE WHEN traffic_type = 'internal' THEN 1 ELSE 0 END) AS is_internal
  FROM purchase_hits
  GROUP BY transaction_id
)
-- Distinct curbside orders by order date, internal and test orders excluded, cancelled orders kept
SELECT
  order_date,
  COUNT(DISTINCT transaction_id) AS curbside_orders
FROM orders
WHERE is_curbside = 1
  AND is_internal = 0
GROUP BY order_date
ORDER BY order_date;

Source Data

Digital Analytics (GA4)

Dependencies

Prerequisite: List the dependencies required for this Metric

Events:

purchase

Dimensions:

fulfillment-method

Report Attributes

Fulfillment Method Store Name Pickup Window Device Category

Recommended Reporting Views

Views
Omnichannel FulfillmentStore Pickup Operations

Segment Eligibility

true

Governance

Data SensitivityInternal

Derived KPIs

Governance

Created bydevyendarm
Created on10/11/2026
Last modified bydevyendarm
Last modified on10/11/2026
StatusPUBLISHED

GitHub

View related Pull Requestcatalog/metrics/curbside-orders.yml

Community Discussion