Portal Community
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

Configuration

Connection

FieldRequiredDescription
accessTokenRequiredPermanent System User access token from Meta Business Manager. Store in BizFirst Credentials Manager and reference via {{ $credentials.whatsAppToken }}.
phoneNumberIdRequiredNumeric string ID of the WhatsApp Business phone number (e.g. 123456789012345).
apiVersionOptionalMeta Graph API version, e.g. v18.0. Defaults to v18.0.

Operation

FieldRequiredDescription
catalogIdRequiredThe numeric Facebook Catalog ID to list products from (e.g. 987654321098765).
limitOptionalNumber of products to return per page. Default is 20. Maximum is 100. Use smaller values for workflows that process each product individually.
cursorOptionalPagination 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

ErrorCause
accessToken is requiredThe accessToken field is empty or missing.
catalogId is requiredThe 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.

FieldTypeDescription
successbooleantrue on the success port.
productsarrayArray of product summary objects. Each object contains id, name, price, and currency.
countintegerNumber of products returned in this page.
cursorstringPagination cursor for the next page. Empty string when on the last page.
statusstring"success" on the success port.
errorCodestringEmpty on success.
errorMessagestringEmpty on success.
payloadstringFull raw JSON response from the Meta Graph API.

Error Port

Fires when the API call fails.

FieldTypeDescription
successbooleanfalse on the error port.
statusstring"failed" or "error".
errorCodestringMeta error code or BizFirst internal validation code.
errorMessagestringHuman-readable error description.
payloadstringRaw 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

ValueExpressionNotes
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 AreaRecommendation
Credential storageAlways store the access token in BizFirst Credentials Manager. Never embed raw tokens in node configuration.
Catalog sync frequencyDo 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 accuracyProduct 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 loopsWhen 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 requirementsProduct 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 handlingAlways 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.