Products
Siren's product records in the REST API: the product object and the routes to list, add, rename, merge, and split products and to set their owners.
WordPress plugin and Siren Cloud
Last updated: October 28, 2026
Siren keeps one record for each product your stores sell, with the collaborators who own it. A store’s copy of a product is a listing, and a merged product holds one listing for each store copy. Owners belong to the product, so they earn on every store that sells it. Assign Product Owners explains how owners earn.
Paths are relative to the REST base, /wp-json/siren/v1 on WordPress, and Authentication explains how to sign requests. The routes need staff access, an administrator or Platform Manager. propertyId names a store, and it’s 0 on WordPress. A route with a product ID in its path answers with the product it joined when that ID was merged. The merge routes refuse a merged ID.
records:products:read reads them, and one with records:products:manage also changes them. A connected WordPress site’s connector token can call GET /products/listing, which is how its “Product owners” boxes show owners. Setting owners, merging, previewing a merge, splitting, and dismissing a match need the Essentials, Plus, or Pro plan, and answer 403 with “Your plan does not include this feature.” on a plan without them. A team member sees only products sold at the Properties they can reach. They get 403 when they add a product to a store they can’t reach, or when a merge or split touches one.
The product object
| Field | Type | Description |
|---|---|---|
id | integer | The product |
name | string | The name Siren shows. Each store keeps its own name on its listing |
mergedIntoId | integer or null | The product this one was merged into |
listings | array | Each store’s listing: id, propertyId, integration, platformId (the store’s own ID for it), name, sku, removed (the store removed it), and lastSeenAt |
owners | array | The owners, each with id and name, up to 10 |
openSuggestions | integer | How many merge suggestions wait for this product |
dateCreated | datetime | When the record was created |
mergedFrom | integer | Only when you asked for a product that was merged: the ID you asked for. The answer is the product it joined |
A WooCommerce variation has no listing of its own. Its sales count for the parent product.
List products
GET /products
| Parameter | Description |
|---|---|
search | Matches the product’s name, a store’s name for it, its SKU, or a store’s ID for it |
propertyId | One store. Leave it out for every store |
integration | One integration, such as wc or edd |
ownerId | Products one collaborator owns |
sold | 1 for products a store sells now |
hasMatches | 1 for products with an open merge suggestion |
page, perPage | Paging. perPage is 1 to 100, default 25 |
Answers { "products": [...], "total", "page", "perPage", "maxOwners", "capability" }. Merged products are left out. maxOwners is 10. capability serves the Products screen and can be null, so ignore it unless you build that screen.
Find the product for a store’s listing
GET /products/listing
Send integration and platformId, the store’s own ID for the product, plus propertyId (0 on WordPress). Answers { "product": {...} }, or { "product": null } when Siren hasn’t seen that listing. For WooCommerce, integration is wc and platformId is the product’s ID in WooCommerce, never a variation’s. When the answer is null, add the product with POST /products.
For example, to set the owners of WooCommerce product 123, look up its Siren product, then send the owners’ collaborator IDs to that product:
GET /wp-json/siren/v1/products/listing?integration=wc&platformId=123&propertyId=0
PUT /wp-json/siren/v1/products/45/owners
{"owners": [12, 15]}
The first call answers {"product": {"id": 45, ...}}, and 45 is the ID the second call uses. If it answers {"product": null}, add the product first with POST /products and a body such as {"propertyId": 0, "integration": "wc", "platformId": "123", "name": "Watercolor course"}, and use the id it answers. Send the bodies as JSON. Collaborators lists your collaborators and their IDs.
Get a product
GET /products/:id
Answers { "product": {...} }. For a product that was merged, it answers with the product it joined, and mergedFrom names the ID you asked for. An unknown ID answers 404.
Add a product a store hasn’t sent
POST /products
Send propertyId (0 on WordPress), integration, platformId, and name. Answers 201 with product. A missing or bad field answers 400, and a store that doesn’t run that integration answers 422.
Rename a product
PATCH /products/:id
Send name. It changes the name Siren shows, not the stores’ names. Answers 200 with product, 400 for a bad name, or 404.
Set the owners
PUT /products/:id/owners
Send owners, a list of collaborator IDs, such as {"owners": [12, 15]}. The list replaces the product’s owners, and an empty list clears them. Answers 200 with product. Repeated IDs count once. A missing owners, or a value that isn’t a list of positive IDs, answers 400. More than 10 owners, or an ID that isn’t a collaborator in your organization, answers 422. An unknown product answers 404. The change applies to sales from then on.
Merge suggestions
GET /products/:id/matches lists the products Siren thinks are the same one, as { "matches": [...] }. Each has id, signal (same_sku or same_name), strength (20 for the same SKU, 10 for the same name), and the other product. Siren suggests matches only between products on different stores.
POST /products/:id/matches/:matchId/dismiss dismisses that suggestion for good and answers the remaining matches.
Merge products
POST /products/merge/preview takes survivorId, the product to keep, and productIds, the products to merge into it. Answers conflicts, the questions a merge would ask, and names, the names the kept product can take.
POST /products/merge takes survivorId, productIds, and optionally name. A merge covers at most 20 products, the survivor included. When the products have different owners, also send owners: combine gives the merged product everyone who owns any of them, as long as that’s 10 or fewer, and survivor keeps only the kept product’s owners. Every listing and sale moves to the survivor. Answers 200 with product.
Without owners when the owners differ, it answers 409. It does the same for an owners answer the question doesn’t offer, such as combine when the owners would come to more than 10, where the question offers only survivor. The error’s context.type is merge_conflict, and context.conflicts lists each question with key, options, and multiple.
A non-text owners or name answers 400. Missing or unknown products, or products you can’t reach, answer 404. Siren refuses some merges with 422: merging a product into itself, no product to merge, more than 20 products, or a product that was already merged.
Split a listing off
POST /products/:id/listings/:listingId/split
Makes that listing a product of its own, with a copy of the current owners and the sales made through it. Answers 201 with the new product. Splitting a product’s only listing answers 422, and an unknown product or listing answers 404.
A collaborator’s products
Request a collaborator with ?fields=products to get the products they own, each with id and name. A merged product shows as the product it joined. Collaborators lists the other fields.
Owners through mappings
The /mappings routes also read and change owners, as Mappings describes. Those calls answer with a Deprecation header and a Link to /products.