Create or update a product
const url = 'https://app.nexym.nl/api/connect/products';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"reference":"ACME-1234","name":"Widget 40 mm, black","sku":"ACME-1234","status":"active","is_online":true}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://app.nexym.nl/api/connect/products \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "reference": "ACME-1234", "name": "Widget 40 mm, black", "sku": "ACME-1234", "status": "active", "is_online": true }'Upserts a product by reference within the key’s project. An existing record is
matched on (project_id, reference); only the fields present in the payload are
written, and any key not listed in the request schema is ignored rather than
rejected.
Requires products: write.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”The write payload for a product. Only these five keys are read; anything else in the body is ignored.
object
Matched against an existing product in this project.
Responses
Section titled “Responses”An existing product was updated.
A universal article record.
object
The canonical article key, unique within the project.
The configurable, bundle or grouped parent this article belongs to.
Per-store content overrides, keyed by store slot and then by field name — the value used when a record is pushed to that store instead of the hub’s own column.
What was last pushed per store, so a re-run does not resend it.
Percentage, two decimals, serialised as a string.
Null means no connected system has said either way yet.
The connection this record was last imported from.
Free-form store for fields the universal schema does not define for everybody: a connector’s declared extension fields and a project’s own customer fields, each under its own name.
A variant as the products listing returns it. Only these columns are selected — the full variant record (barcodes, options, weight, labels, meta) is not exposed by this API.
object
Related products, in the source’s order. The hub product’s SKU when that product is in the hub, otherwise the SKU the source gave.
Up-sells, in the source’s order, as SKUs (see related_skus).
Cross-sells, in the source’s order, as SKUs (see related_skus).
Example
{ "id": 4821, "project_id": 3, "reference": "ACME-1234", "sku": "ACME-1234", "name": "Widget 40 mm, black", "type": "simple", "parent_reference": null, "attribute_set": "default", "description": "A 40 mm widget in powder-coated black.", "short_description": "40 mm widget, black", "url": "/widgets/acme-1234", "meta_title": "Widget 40 mm, black", "meta_description": "Buy the 40 mm black widget.", "labels": { "nl_main": { "name": "Widget 40 mm, zwart" } }, "pushed_store_labels": { "nl_main": { "name": "Widget 40 mm, zwart" } }, "brand": "ACME", "brand_name": "ACME Tools", "category_reference": "CAT-WIDGETS", "supplier_reference": "SUP-0042", "vat_rate": "21.00", "is_online": true, "status": "active", "source_connection_id": 7, "meta": { "service_code": "INSTALL-STD" }, "source_changed_at": "2026-09-18T09:12:41.000000Z", "created_at": "2026-08-01T11:03:27.000000Z", "updated_at": "2026-09-18T09:12:41.000000Z", "variants": [ { "id": 90412, "product_id": 4821, "reference": "ACME-1234-BLK", "sku": "ACME-1234-BLK", "barcode": "8712345678906" } ], "related_skus": [ "ACME-2000", "ACME-2001" ], "upsell_skus": [ "ACME-1234-PRO" ], "crosssell_skus": []}A new product was created.
A universal article record.
object
The canonical article key, unique within the project.
The configurable, bundle or grouped parent this article belongs to.
Per-store content overrides, keyed by store slot and then by field name — the value used when a record is pushed to that store instead of the hub’s own column.
What was last pushed per store, so a re-run does not resend it.
Percentage, two decimals, serialised as a string.
Null means no connected system has said either way yet.
The connection this record was last imported from.
Free-form store for fields the universal schema does not define for everybody: a connector’s declared extension fields and a project’s own customer fields, each under its own name.
A variant as the products listing returns it. Only these columns are selected — the full variant record (barcodes, options, weight, labels, meta) is not exposed by this API.
object
Related products, in the source’s order. The hub product’s SKU when that product is in the hub, otherwise the SKU the source gave.
Up-sells, in the source’s order, as SKUs (see related_skus).
Cross-sells, in the source’s order, as SKUs (see related_skus).
Example
{ "id": 4821, "project_id": 3, "reference": "ACME-1234", "sku": "ACME-1234", "name": "Widget 40 mm, black", "type": "simple", "parent_reference": null, "attribute_set": "default", "description": "A 40 mm widget in powder-coated black.", "short_description": "40 mm widget, black", "url": "/widgets/acme-1234", "meta_title": "Widget 40 mm, black", "meta_description": "Buy the 40 mm black widget.", "labels": { "nl_main": { "name": "Widget 40 mm, zwart" } }, "pushed_store_labels": { "nl_main": { "name": "Widget 40 mm, zwart" } }, "brand": "ACME", "brand_name": "ACME Tools", "category_reference": "CAT-WIDGETS", "supplier_reference": "SUP-0042", "vat_rate": "21.00", "is_online": true, "status": "active", "source_connection_id": 7, "meta": { "service_code": "INSTALL-STD" }, "source_changed_at": "2026-09-18T09:12:41.000000Z", "created_at": "2026-08-01T11:03:27.000000Z", "updated_at": "2026-09-18T09:12:41.000000Z", "variants": [ { "id": 90412, "product_id": 4821, "reference": "ACME-1234-BLK", "sku": "ACME-1234-BLK", "barcode": "8712345678906" } ], "related_skus": [ "ACME-2000", "ACME-2001" ], "upsell_skus": [ "ACME-1234-PRO" ], "crosssell_skus": []}No key, a key that does not match, a revoked key, or an expired one. The body says which, and nothing else — an unusable key is never described further.
Every error this API returns carries a single human-readable message.
object
Examples
No bearer token, or one too short to hold a prefix
{ "message": "Missing or invalid API key."}Unknown key, or a failed hash check
{ "message": "Invalid API key."}The key was revoked in the dashboard
{ "message": "API key has been revoked."}The key's expiry has passed
{ "message": "API key has expired."}The key is valid but its abilities do not cover this resource at this right.
Every error this API returns carries a single human-readable message.
object
Examples
{ "message": "This API key lacks read access to orders."}{ "message": "This API key lacks write access to stock."}The payload failed validation. The framework’s standard shape: a summary
message plus an errors map of field to messages. Requires
Accept: application/json — without it the response is a 302 redirect instead.
object
Field name to the list of messages that field failed on.
object
Examples
{ "message": "The reference field is required. (and 1 more error)", "errors": { "reference": [ "The reference field is required." ], "name": [ "The name field is required." ] }}The per-minute rate limit was exceeded. The limit is 120 requests per minute by
default (CONNECT_API_RATE_PER_MIN), counted per API key — or per IP address for
a request that never authenticated.
Every error this API returns carries a single human-readable message.
object
Examples
{ "message": "Too many requests — slow down."}Headers
Section titled “Headers”Seconds until the limit resets.
Requests allowed per minute.
Requests left in the current window.