WhatsApp Commerce Cart orders: Orders returned by this node are customer cart orders submitted via the WhatsApp native shopping cart, not orders received as inbound webhook messages (which are handled by the commerce/receiveOrder trigger). Use this node for batch polling and reporting workflows.
When to Use
- Order fulfilment polling: A scheduled workflow runs every 15 minutes and calls this node with
status: "pending" to retrieve all new unprocessed orders. Each order is then passed to the fulfilment system for picking, packing, and shipping, with the order status updated to "shipped" upon dispatch.
- Daily order reconciliation: A nightly reconciliation workflow calls this node with
since and until date filters to pull all orders for the previous day and reconciles them against the orders recorded in the internal OMS (Order Management System), flagging any discrepancies.
- Cart abandonment detection: A workflow polls for orders in
pending status that are older than 2 hours, identifies customers who have not completed payment, and sends a follow-up template message encouraging them to complete the purchase.
- Revenue reporting: A weekly analytics workflow pages through all orders from the past 7 days and aggregates totals by product, category, and customer region, feeding a commerce performance dashboard.
- Customer satisfaction outreach: A post-delivery workflow polls for orders with
status: "delivered" that are 3 days old and sends each customer a satisfaction survey template message, triggering the survey workflow for each completed order.
Configuration
Connection
| Field | Required | Description |
accessToken | Required | Permanent System User access token from Meta Business Manager. Store in BizFirst Credentials Manager and reference via {{ $credentials.whatsAppToken }}. |
phoneNumberId | Required | Numeric string ID of the WhatsApp Business phone number (e.g. 123456789012345). Orders are scoped to the WABA associated with this phone number. |
apiVersion | Optional | Meta Graph API version, e.g. v18.0. Defaults to v18.0. |
Operation
| Field | Required | Description |
limit | Optional | Number of orders per page. Default is 20. Use smaller values when each order requires heavy downstream processing. |
after | Optional | Pagination cursor from the previous call's cursor output. Omit on the first call. |
status | Optional | Filter by order status. Accepted values: pending, shipped, delivered, cancelled. Omit to return orders of all statuses. |
since | Optional | ISO 8601 datetime string (e.g. 2025-05-01T00:00:00Z). Returns only orders created on or after this timestamp. |
until | Optional | ISO 8601 datetime string (e.g. 2025-05-31T23:59:59Z). Returns only orders created before or at this timestamp. |
Sample Configuration
{
"resource": "commerce",
"operation": "getOrders",
"accessToken": "{{ $credentials.whatsAppToken }}",
"phoneNumberId": "123456789012345",
"status": "pending",
"since": "{{ $vars.lastPollTime }}",
"limit": "50"
}
Validation Errors
| Error | Cause |
accessToken is required | The accessToken field is empty or missing. |
phoneNumberId is required | The phoneNumberId field is empty or missing. |
100 (Meta) | Invalid phone number ID or the System User does not have access to this WABA's orders. |
200 (Meta) | Permissions error — the token lacks order management permissions. |
190 (Meta) | Access token expired or revoked. |
invalid status filter (Meta) | The status value is not one of: pending, shipped, delivered, cancelled. |
invalid date format (Meta) | The since or until value is not a valid ISO 8601 datetime string. |
Output
Success Port
Fires when Meta returns order data. The orders array contains one summary entry per order. Use the cursor field for pagination. When cursor is empty, you have retrieved the last page.
| Field | Type | Description |
success | boolean | true on the success port. |
orders | array | Array of order summary objects. Each contains id, status, created_time, and estimated_total. |
count | integer | Number of orders in this page. |
cursor | string | Pagination cursor for the next page. Empty string when on the last page. |
status | string | "success" on the success port. |
errorCode | string | Empty on success. |
errorMessage | string | Empty on success. |
payload | string | Full raw JSON response from the Meta Graph API. |
Error Port
Fires when the API call fails.
| Field | Type | Description |
success | boolean | false on the error port. |
status | string | "failed" or "error". |
errorCode | string | Meta error code or BizFirst internal validation code. |
errorMessage | string | Human-readable error description. |
payload | string | Raw API error response for diagnostics. |
Sample Output
{
"success": true,
"orders": [
{
"id": "order_112233",
"status": "pending",
"created_time": "2025-05-26T08:12:44Z",
"estimated_total": "7998"
},
{
"id": "order_112234",
"status": "pending",
"created_time": "2025-05-26T08:45:10Z",
"estimated_total": "2999"
}
],
"count": 2,
"cursor": "QVFIUmNSbXJkT2xNQ1...",
"status": "success",
"errorCode": "",
"errorMessage": "",
"payload": "{\"data\":[...],\"paging\":{\"cursors\":{\"after\":\"QVFIUmNSbXJkT2xNQ1...\"}}}"
}
Expression Reference
| Value | Expression | Notes |
| Orders array | {{ $output.getOrders.orders }} | Pass to a ForEach node to process each order individually, e.g. routing each to commerce/getOrder for full details. |
| Order count | {{ $output.getOrders.count }} | Use to decide whether to continue polling. A count of 0 with no cursor means no new orders since last poll. |
| Next page cursor | {{ $output.getOrders.cursor }} | Pass as the after input on the next call. Empty string signals the last page. |
| First order ID | {{ $output.getOrders.orders[0].id }} | Use to initiate a single-order detail lookup via commerce/getOrder. |
| Error code | {{ $output.getOrders.errorCode }} | Non-empty on error port. Log for order polling diagnostics. |
Node Policies & GuardRails
| Policy Area | Recommendation |
| Credential storage | Always store the access token in BizFirst Credentials Manager. Never embed raw tokens in node configuration fields. |
| Order idempotency | Track processed order IDs in a database. When polling for new orders, only process order IDs that have not been seen before. Polling the same time window twice may return the same orders — always check against your processed orders table before sending fulfilment requests to the OMS. |
| Poll frequency | Do not poll more than once per minute. For real-time order ingestion, prefer webhook-based order receipt via commerce/receiveOrder. Use this node for batch polling and catch-up scenarios only. |
| Date range accuracy | Use since set to the timestamp of the last successful poll, not the current time minus an interval. Store the last successful poll timestamp in a persistent variable to avoid gaps caused by workflow failures or delays. |
| Error port handling | Always connect the error port. A polling failure should not advance the lastPollTime variable — leave it at the previous value so the next poll re-fetches the missed window. |
| Order fulfilment | This node returns order summaries only. For fulfilment, call commerce/getOrder per order ID to retrieve the full item list and customer details before submitting to the OMS. |
Examples
Pending Order Fulfilment Polling
{
"resource": "commerce",
"operation": "getOrders",
"accessToken": "{{ $credentials.whatsAppToken }}",
"phoneNumberId": "123456789012345",
"status": "pending",
"since": "{{ $vars.lastOrderPollTime }}",
"limit": "50"
}
A ScheduledTrigger fires every 15 minutes. This node fetches all pending orders since the last successful poll. On success, the workflow stores the current UTC timestamp as lastOrderPollTime. A ForEach node passes each order ID to commerce/getOrder to retrieve item details. A deduplication check against the processed_orders table ensures each order is sent to the OMS exactly once.
Cart Abandonment Recovery
{
"resource": "commerce",
"operation": "getOrders",
"accessToken": "{{ $credentials.whatsAppToken }}",
"phoneNumberId": "123456789012345",
"status": "pending",
"since": "{{ $vars.twoHoursAgo }}",
"until": "{{ $vars.oneHourAgo }}",
"limit": "100"
}
A workflow scheduled every hour retrieves pending orders that are between 1 and 2 hours old — these represent carts that were started but not completed. A ForEach node calls commerce/getOrder for each to retrieve the customer phone and items. A template message is sent to each customer via message/sendTemplate with a personalised cart recovery message. Orders already contacted are tracked in a cart_recovery_attempts table to prevent multiple messages to the same customer.
Daily Order Revenue Report
{
"resource": "commerce",
"operation": "getOrders",
"accessToken": "{{ $credentials.whatsAppToken }}",
"phoneNumberId": "123456789012345",
"since": "{{ $vars.yesterdayStart }}",
"until": "{{ $vars.yesterdayEnd }}",
"limit": "100",
"after": "{{ $vars.nextPageCursor }}"
}
A nightly report workflow paginates through all orders from the previous day. Each page's cursor is stored in nextPageCursor to drive the next loop iteration. After collecting all order IDs, a second loop calls commerce/getOrder per order to aggregate item quantities and revenue by product. The aggregated report is written to a reporting database and emailed to the commerce team at 07:00.