Pagination: Results are returned in pages. The cursor field in the output contains the token for the next page. Pass it as the cursor input on the next call to fetch the following batch. When cursor is empty in the output, you have reached the last page.
When to Use
- ERP/PIM reconciliation: A nightly reconciliation workflow fetches all products from the Facebook Catalog page by page and compares them against the master product list in the ERP system, identifying items that are stale, missing, or have incorrect prices.
- Inventory audit: A stock management workflow lists all products in the catalog and cross-references availability statuses against the warehouse management system, flagging products that are marked "in stock" in the catalog but have zero inventory in the warehouse.
- Dynamic product showcase: A customer engagement workflow lists available products in a category and composes a WhatsApp carousel message using the product IDs from this node's output as inputs to
message/sendInteractive.
- Promotional price verification: Before launching a promotional campaign, a QA workflow fetches all products and verifies that discounted prices have been correctly applied, alerting the merchandising team to any items still showing the original price.
- Catalog export and backup: A data export workflow pages through the entire catalog and writes each product to a cloud storage CSV, providing a restorable backup of all product data independent of Meta's Commerce Manager.
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). |
apiVersion | Optional | Meta Graph API version, e.g. v18.0. Defaults to v18.0. |
Operation
| Field | Required | Description |
catalogId | Required | The numeric Facebook Catalog ID to list products from (e.g. 987654321098765). |
limit | Optional | Number of products to return per page. Default is 20. Maximum is 100. Use smaller values for workflows that process each product individually. |
cursor | Optional | Pagination cursor from the previous call's cursor output field. Omit on the first call. Pass the value from {{ $output.getProducts.cursor }} to advance to the next page. |
Sample Configuration
{
"resource": "commerce",
"operation": "getProducts",
"accessToken": "{{ $credentials.whatsAppToken }}",
"phoneNumberId": "123456789012345",
"catalogId": "987654321098765",
"limit": "50",
"cursor": "{{ $input.nextPageCursor }}"
}
Validation Errors
| Error | Cause |
accessToken is required | The accessToken field is empty or missing. |
catalogId is required | The catalogId field is empty or missing. |
100 (Meta) | Invalid catalog ID or the System User does not have access to this catalog. |
200 (Meta) | Permissions error — the token lacks catalog_management permission. |
190 (Meta) | Access token expired or revoked. |
invalid cursor (Meta) | The cursor value is stale or malformed. Restart pagination from the first page by omitting the cursor. |
Output
Success Port
Fires when Meta returns product data. The products array contains one entry per product in the current page. Use the cursor field to fetch the next page. When cursor is empty, pagination is complete.
| Field | Type | Description |
success | boolean | true on the success port. |
products | array | Array of product summary objects. Each object contains id, name, price, and currency. |
count | integer | Number of products returned 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,
"products": [
{ "id": "1234567890", "name": "Classic Polo Shirt", "price": "2999", "currency": "USD" },
{ "id": "1234567891", "name": "Slim Fit Chinos", "price": "4999", "currency": "USD" },
{ "id": "1234567892", "name": "Canvas Sneakers", "price": "5999", "currency": "USD" }
],
"count": 3,
"cursor": "QVFIUmpkNFllQlhNQ1...",
"status": "success",
"errorCode": "",
"errorMessage": "",
"payload": "{\"data\":[...],\"paging\":{\"cursors\":{\"after\":\"QVFIUmpkNFllQlhNQ1...\"}}}"
}
Expression Reference
| Value | Expression | Notes |
| Products array | {{ $output.getProducts.products }} | Full array of product summaries. Pass to a ForEach node for per-product processing. |
| Product count | {{ $output.getProducts.count }} | Number of products in this page. Use to detect an empty last page. |
| Next page cursor | {{ $output.getProducts.cursor }} | Pass as the cursor input of the next commerce/getProducts call. Empty on the last page. |
| First product ID | {{ $output.getProducts.products[0].id }} | Access individual product IDs for use in product message nodes. |
| Error code | {{ $output.getProducts.errorCode }} | Non-empty on error port. Log for catalog access 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. |
| Catalog sync frequency | Do not paginate through large catalogs more often than needed. Cache the full product list in a database after each sync run and reference the cache in workflows that only need read access. |
| Inventory accuracy | Product availability shown in products[].price reflects what is stored in Meta's catalog. Always sync catalog products from your authoritative inventory system before using them in customer-facing messages. |
| Pagination loops | When iterating all pages in a loop, always check that {{ $output.getProducts.cursor }} is non-empty before issuing the next call. Failure to check will cause infinite loops on the last page. |
| Image CDN requirements | Product image_url values returned by this node must be hosted on a publicly accessible CDN. Images behind authentication, firewalls, or expired URLs will fail to render in WhatsApp product messages. |
| Error port handling | Always connect the error port. A failed catalog read in a pagination loop should halt the loop and trigger a retry workflow rather than silently skipping items. |
Examples
First Page — List Top 20 Products
{
"resource": "commerce",
"operation": "getProducts",
"accessToken": "{{ $credentials.whatsAppToken }}",
"phoneNumberId": "123456789012345",
"catalogId": "987654321098765",
"limit": "20"
}
Fetches the first 20 products from the catalog. The output cursor is stored as a workflow variable nextCursor. An IfCondition node checks whether {{ $output.getProducts.cursor }} is non-empty to decide whether to loop and fetch the next page.
Paginated Full Catalog Export
{
"resource": "commerce",
"operation": "getProducts",
"accessToken": "{{ $credentials.whatsAppToken }}",
"phoneNumberId": "123456789012345",
"catalogId": "987654321098765",
"limit": "100",
"cursor": "{{ $vars.nextCursor }}"
}
This node is placed inside a loop. On each iteration, $vars.nextCursor is set from the previous call's cursor output. The loop exit condition is {{ $output.getProducts.cursor }} === "". Each page of 100 products is appended to a running array and written to a database table after the loop completes.
Inventory Reconciliation Against ERP
{
"resource": "commerce",
"operation": "getProducts",
"accessToken": "{{ $credentials.whatsAppToken }}",
"phoneNumberId": "123456789012345",
"catalogId": "987654321098765",
"limit": "50"
}
A reconciliation workflow calls this node to fetch the current catalog product list, then queries the ERP database for the same SKUs. A DataCompare node finds discrepancies between catalog prices and ERP prices. Products with mismatched prices are queued for update via commerce/updateProduct. Results are emailed to the merchandising team as a daily diff report.