From 3eeeb7dd03220ef831a54a248149bf7783224b44 Mon Sep 17 00:00:00 2001 From: Rajeh Taher Date: Sat, 26 Sep 2026 23:03:04 +0200 Subject: [PATCH 1/2] feat(sdk): read linked catalog product pages --- contracts/README.md | 7 ++++ contracts/cloud-api-supplements.json | 8 ++++ packages/typescript/README.md | 7 ++++ packages/typescript/src/index.ts | 3 ++ .../src/messaging/cloud-catalogs.ts | 39 +++++++++++++++++++ 5 files changed, 64 insertions(+) diff --git a/contracts/README.md b/contracts/README.md index d554da24..da37634b 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -71,6 +71,13 @@ are recorded in `cloud-api-supplements.json`. The existing typed including when callers supply an idempotency key or retry override. The full snapshot remains pinned to PR #310 until the API branches converge. +API PR #380 at `bbfb2f4af3a8e1289074d9b7d0ed058b96881410` adds the +read-only linked-catalog product page. `cloud-api-supplements.json` records the +exact Graph OpenAPI hash. `MessagingClient.cloudCatalogs.listProducts` follows +its WABA and catalog path, `sessions:read` credential boundary, projected +product fields, and opaque cursor response. This does not change the pinned +full snapshot or establish live catalog access. + The merged Campaigns failed-state follow-up updates the `campaign.failed` reason example and the public archive/delete `409` descriptions. It changes no operation fingerprint or SDK method mapping. diff --git a/contracts/cloud-api-supplements.json b/contracts/cloud-api-supplements.json index a9a32129..429c69b0 100644 --- a/contracts/cloud-api-supplements.json +++ b/contracts/cloud-api-supplements.json @@ -137,6 +137,14 @@ "operationIds": [], "schemas": ["CloudAccountStatusPayload"], "merged": false + }, + { + "pullRequest": "https://github.com/polymorfa/polymorfa/pull/380", + "commit": "bbfb2f4af3a8e1289074d9b7d0ed058b96881410", + "sourcePath": "docs/api-reference/openapi.graph.json", + "sha256": "b9d3778f17a90fd0c58e0cb1b3f6ef689661980fa83ad7565bc55ae4ab93fab4", + "operationIds": ["graphListCatalogProducts"], + "merged": false } ] } diff --git a/packages/typescript/README.md b/packages/typescript/README.md index 659ad32e..b24f1e3c 100644 --- a/packages/typescript/README.md +++ b/packages/typescript/README.md @@ -387,6 +387,13 @@ requires `sessions:read` and Graph access. The response retains opaque cursors; it does not expose upstream pagination URLs or grant merchant ownership. Use an organization API key or project token, with the latter confined to its project. +`messaging.cloudCatalogs.listProducts(wabaId, catalogId, { version: "v26.0", limit: 25, after })` +reads a page of product IDs, optional retailer IDs, names, and availability +from a catalog linked to that WABA. It has the same server-credential and +`sessions:read` requirements. Use the returned opaque `paging.cursors.after` +to request the next page. The API rejects catalogs it cannot verify as linked; +listing products does not grant permission to send them. + `messaging.flowEncryption.retrieve(phoneNumberId, { version: "v26.0" })` reads the registered public key and Meta signature status with `sessions:read`. `messaging.flowEncryption.register(phoneNumberId, diff --git a/packages/typescript/src/index.ts b/packages/typescript/src/index.ts index 617352d5..6fb4473f 100644 --- a/packages/typescript/src/index.ts +++ b/packages/typescript/src/index.ts @@ -69,8 +69,11 @@ export type { } from "./messaging/flow-encryption.js"; export type { CloudProductCatalog, + CloudCatalogProduct, ListCloudCatalogsParams, ListCloudCatalogsResponse, + ListCloudCatalogProductsParams, + ListCloudCatalogProductsResponse, } from "./messaging/cloud-catalogs.js"; export { BridgeClient, diff --git a/packages/typescript/src/messaging/cloud-catalogs.ts b/packages/typescript/src/messaging/cloud-catalogs.ts index dd9e0c33..67aaf51a 100644 --- a/packages/typescript/src/messaging/cloud-catalogs.ts +++ b/packages/typescript/src/messaging/cloud-catalogs.ts @@ -20,6 +20,19 @@ export interface ListCloudCatalogsResponse { readonly cursors: { readonly before?: string; readonly after?: string }; }; } +export interface CloudCatalogProduct { + readonly id: string; + readonly retailer_id?: string; + readonly name?: string; + readonly availability?: string; +} +export type ListCloudCatalogProductsParams = ListCloudCatalogsParams; +export interface ListCloudCatalogProductsResponse { + readonly data: readonly CloudCatalogProduct[]; + readonly paging?: { + readonly cursors: { readonly before?: string; readonly after?: string }; + }; +} /** Reads catalog links visible to the authenticated WABA. Requires sessions:read. */ export class CloudCatalogsResource { @@ -51,4 +64,30 @@ export class CloudCatalogsResource { ...options, }); } + + /** Read a page of products only after the API verifies that this catalog is linked to the WABA. */ + listProducts( + wabaId: string, + catalogId: string, + params: ListCloudCatalogProductsParams, + options: RequestOptions = {}, + ): Promise> { + if (this.credentialType === "clientToken") { + throw new PolymorfaConfigurationError( + "Official API catalogs require an organization API key or project token.", + "credential", + ); + } + if (!wabaId.trim() || !/^[0-9]+$/.test(catalogId) || !params.version.trim()) { + throw new PolymorfaConfigurationError( + "Provide a WABA ID, numeric catalog ID, and Graph version.", + ); + } + return this.transport.request({ + method: "GET", + path: `/graph/whatsapp/${encodeURIComponent(params.version)}/${encodeURIComponent(wabaId)}/product_catalogs/${encodeURIComponent(catalogId)}/products`, + query: { limit: params.limit, after: params.after }, + ...options, + }); + } } From 39d87176503b8d3e7a7924baef1a9f83ed716bb2 Mon Sep 17 00:00:00 2001 From: Rajeh Taher Date: Sat, 26 Sep 2026 23:29:35 +0200 Subject: [PATCH 2/2] style(sdk): format Cloud catalog resource --- packages/typescript/src/messaging/cloud-catalogs.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/typescript/src/messaging/cloud-catalogs.ts b/packages/typescript/src/messaging/cloud-catalogs.ts index 67aaf51a..a7afa9b0 100644 --- a/packages/typescript/src/messaging/cloud-catalogs.ts +++ b/packages/typescript/src/messaging/cloud-catalogs.ts @@ -78,7 +78,11 @@ export class CloudCatalogsResource { "credential", ); } - if (!wabaId.trim() || !/^[0-9]+$/.test(catalogId) || !params.version.trim()) { + if ( + !wabaId.trim() || + !/^[0-9]+$/.test(catalogId) || + !params.version.trim() + ) { throw new PolymorfaConfigurationError( "Provide a WABA ID, numeric catalog ID, and Graph version.", );