FacetFlux
verified 22 may 2026The Particle Accelerator for Product Data
TL;DR
FacetFlux is an API-first, headless Product Information Management (PIM) platform designed for mid-market e-commerce brands ($20M–$200M GMV). It differentiates itself through a graph-based architecture and a unique 'Dirty Data' staging layer that uses AI to clean and map messy supplier data before it reaches the core system.
What Users Actually Pay
No user-reported pricing yet.
Our Take
Where most PIMs treat product data as documents to author, FacetFlux treats it as a schema problem — a typed graph that supplier feeds map into, with a staging layer that lets messy CSVs and PDFs land without touching the golden record. Built for the mid-market e-commerce team that outgrew Excel years ago but finds enterprise PIMs too rigid to keep pace with shifting category trees and attribute sets. The "columns, not rows" framing is the bet: schema-first wins when the model itself is the thing changing. Strongest fit for teams onboarding suppliers continuously rather than maintaining a static catalog.
Pros
- + AI-driven 'Dirty Data' staging eliminates the need for pre-import data cleaning in Excel.
- + Graph-based architecture supports complex, non-linear product data models without performance lag.
- + Ultra-fast 200ms synchronization is ideal for real-time inventory and pricing across multiple channels.
- + A modern, spreadsheet-like browser interface simplifies bulk editing for non-technical merchandising teams.
Cons
- - Currently lacks the extensive third-party plugin ecosystem found in more mature platforms like Akeneo.
- - Implementation requires more development resources than 'all-in-one' monolithic PIM solutions.
- - High entry price point ($20M+ GMV target) makes it inaccessible for small-to-midsize businesses.
- - Relatively new to the market, which may cause hesitation for risk-averse enterprise IT departments.
Sentiment Analysis
Sentiment has remained stable since last capture. Sentiment is highly positive, showing a slight increase from the previous 0.82. Users are particularly impressed by the speed and the AI staging features, though some 'early adopter' friction regarding the breadth of native integrations is noted.
Sentiment Over Time
By Source
12 mentions
Sample quotes (2)
- "The 'Dirty Data' layer is a game-changer for those of us dealing with messy dropshipping feeds."
- "Finally a headless PIM that doesn't feel like it was built in the 2000s."
25 mentions
Sample quotes (2)
- "FacetFlux is basically the Linear of PIMs. Fast, focused, and actually understands the modern e-comm workflow."
- "Seeing 200ms sync times for a 50k SKU catalog is wild."
2 mentions
Sample quotes (2)
- "Powerful API and very fast, but still early days for native integrations."
- "Exactly what we needed for our headless Shopify build."
Agent Readiness
52/100FacetFlux is highly agent-ready due to its 'API-first' and 'Headless' architecture. The combination of comprehensive REST/GraphQL APIs, OAuth2 support, and extensive webhook capabilities allows AI agents to perform complex data orchestration, mapping, and synchronization tasks with minimal friction. Its graph-based backend makes it particularly well-suited for autonomous agents that need to navigate complex product hierarchies.
Last checked Apr 30, 2026
MCP Integrations
1 server44 toolsAllows AI tools like Codex, Claude Code et al. to help manage your product data
44 tools
pim.schemas.set.productNumberTemplateSet the schema's product-number template. C = category code character, N = sequential digit, any other character = literal. Example: 'CCCC-NNNNNNNN' renders 'PUMP-00000001'. Pass null to clear. Response: { schemaId, productNumberTemplate }. Errors: { error: { code: 'not_found', ... } }.pim.products.byExternalId.getGet a single product by its source system + externalId. Useful for reconciling products imported from a supplier feed or upstream system without first calling pim.products.search. Same response shape as pim.products.get. Response: { product?: {...}, attributes?: [...], variants?: [...], channelStatuses?: [...] }. Errors: { error: { code: 'not_found' | 'bad_input', ... } }.pim.attributeDefinitions.createCreate an attribute definition in a schema, or return the existing one if `code` already exists. DataType is required and immutable once values exist. Optional fields: traitId (binds the def to a trait), unit, scope (product|variantDefining|perVariant), role (None|Name|Description|Brand|...), isRequired, isTranslatable, isSearchable, isFilterable. To populate enum allowed values use pim.attributeDefinitions.allowedValues.set after creation. Response: { attributeDefinition: {...}, created: bool }. Errors: { error: { code: 'not_found' | 'bad_input', ... } }.pim.traits.createCreate a trait in a schema, or return the existing one if `code` already exists. Traits are reusable groups of attribute definitions; products opt in to traits to gain their attributes. To bind attribute definitions to the trait after creation, use pim.traits.attributes.add (or set `traitId` directly when creating attribute definitions). Response: { trait: {...}, created: bool }. Errors: { error: { code: 'not_found' | 'bad_input', ... } }.pim.products.traits.addAssign one or more traits to a product. Traits add their attribute definitions to the product's editable surface. Idempotent. Response: { productId, traitIds, added: count }. Errors: { error: { code: 'not_found', ... } }.pim.products.batch.createBulk create-or-upsert products by (sourceSystem, externalId). All inputs share the same schemaId and sourceSystem. Up to 200 products per call; route larger payloads through the Phase-3 imports.* path. Per-row results report which were created vs updated. Channel-code application is a second pass — per-row failures appear in `channelWarnings` (typically: an unknown channel code) without failing the batch; the product itself is still created/updated. Response: { sourceSystem, schemaId, results: [{ productId, externalId, action }], channelWarnings: [{ externalId, productId, message }] }. Errors: { error: { code: 'not_found' | 'bad_input', ... } }.pim.products.categories.removeRemove product from one or more categories. Idempotent — non-assigned categories are no-ops. Response: { productId, categoryIds, removed: count }. Errors: { error: { code: 'not_found', ... } }.pim.products.channels.addPublish product on one or more channels. Channel codes must already exist for the tenant (otherwise the underlying invariant rejects the write). Idempotent. Response: { productId, channelCodes, added: count }. Errors: { error: { code: 'not_found' | 'unprocessable', ... } }.pim.traits.getGet a trait by id, with its trait-scoped attribute definitions resolved. Response: { trait: {...}, attributeDefinitions: [...] }. Errors: { error: { code: 'not_found', ... } }.imports.smartImports.deleteDelete a SmartImport record. Does NOT delete any PIM data created during execution — only the import record itself. Response: { smartImportId, deleted: bool }. Errors: { error: { code: 'not_found', ... } }.pim.products.traits.removeUnassign one or more traits from a product. Note: existing attribute values for the trait's attributes are NOT deleted automatically — call pim.products.attributes.set with mode=replace afterwards if you want to drop them. Idempotent. Response: { productId, traitIds, removed: count }. Errors: { error: { code: 'not_found', ... } }.imports.mappings.applyPure transform: apply column→attribute mappings to a list of source records and return per-row attribute payloads ready for downstream writes. NO DB writes happen here. Each rule maps `sourceColumn` (a key in the record) to `destinationAttributeDefinitionId` (an attribute definition in the target schema), with an optional `role` tag (uniqueIdentifier, productGroup, name, brand, category, mainImage, productType). The mapping engine performs type coercion against the destination's DataType (e.g. string '42' → 42 for Number) and reports per-row errors and warnings. Recommended downstream flow: use the role-tagged values (especially uniqueIdentifier) as the externalId for `pim.products.batch.create`, then write the per-row attributes via `pim.products.attributes.set`. Response: { targetSchemaId, ruleCount, recordCount, skippedRules: [{ ruleIndex, reason }], items: [{ rowIndex, isSuccess, attributes: [{ attributeDefinitionId, code, dataType, value }], roles: { [role]: value }, errors: [...], warnings: [...] }] }. Errors: { error: { code: 'not_found' | 'bad_input', ... } }.imports.smartImports.listList SmartImports for the current tenant, newest first. Excludes the heavy `rawProducts` list from each entry — call imports.smartImports.get to fetch one in full. Optional `status` filter narrows by lifecycle state. Response: { smartImports: [{ id, status, mode, originalFileName, productCount, variantCount, createdAt, updatedAt }] }. Errors: { error: { code: 'bad_input', ... } }.jobs.getPoll a background job's state. Status values: queued, processing, success, failed. `progress` is a 0.0–1.0 fraction (null when the job hasn't reported any). Tenant-scoped: agents can only see jobs in their own tenant. Response: { jobId, kind, status, progress, retryCount, started, lastAlive, cancellationRequested }. Errors: { error: { code: 'not_found', ... } }.pim.attributeDefinitions.allowedValues.setReplace the allowed-value list for an enum/multiEnum attribute definition. Idempotent on `value`: values present in input but not in storage are inserted, values present in both have their labels/sortOrder/isActive updated, values present in storage but absent from input are deleted. After this call HasAllowedValues is true iff the resulting list is non-empty. Response: { attributeDefinitionId, added, updated, deleted }. Errors: { error: { code: 'not_found' | 'unprocessable', ... } }.pim.products.categories.addAdd product to one or more categories. Idempotent — already-assigned categories are no-ops. Response: { productId, categoryIds, added: count }. Errors: { error: { code: 'not_found', ... } }.pim.products.channels.removeUnpublish product from one or more channels. Idempotent. Response: { productId, channelCodes, removed: count }. Errors: { error: { code: 'not_found', ... } }.pim.schemas.getGet a PIM schema by id, with productTypes, traits, attributeDefinitions, and channels embedded. Use the optional `fields` param to limit the response to specific top-level keys when the full payload is too large. Allowed `fields` values: schema, productTypes, traits, attributeDefinitions, channels. Response: { schema?: {...}, productTypes?: [...], traits?: [...], attributeDefinitions?: [...], channels?: [...] }. Errors: { error: { code: 'not_found' | 'forbidden', ... } }.imports.smartImports.startUpload + analyze inline file content. Supported formats: 'csv' (text/csv) and 'wawiJson' (WAWI-style JSON product feed with FTITLES/FVALUES/DETAIL fields). XLSX is intentionally not supported via MCP in v1 — agents that need XLSX should convert to CSV client-side or use the HTTP API. Returns a SmartImport in Analyzed state with field inventory ready for inference. Response: { smartImport: {...} }. Errors: { error: { code: 'bad_input' | 'unprocessable', ... } }.pim.products.attributes.setSet or merge attribute values on a product (or a specific variant). `mode=merge` (default) upserts only the provided values; existing values for other attributes are left untouched. `mode=replace` upserts the provided values AND deletes any other existing attribute values on the entity, leaving exactly the provided set. Each value is sent as native JSON matching its definition's DataType (string for Text, number for Number/Decimal, bool for Boolean, ["..."] for MultiEnum / arrays, {"en":"...","de":"..."} for translatable Text). When `variantId` is set, the variant must belong to `productId` and the same tenant; attribute scope must match the target entity (product-scoped defs onto product, variantDefining/perVariant defs onto variant) — mismatches are reported in `skipped` with reason `scope_mismatch`. Other skip reasons: `missing_attribute_definition_id`, `definition_not_in_schema`, `value_parse_error: <detail>`. In `mode=replace`, attributes the caller listed but whose value couldn't parse are NOT deleted — only attributes the caller didn't mention at all. Response: { productId, variantId, applied, deleted, skipped: [{ attributeDefinitionId, reason }] }. Errors: { error: { code: 'not_found' | 'bad_input' | 'unprocessable', ... } }.pim.products.status.setSet the product's lifecycle status. Allowed values: draft, active, archived, requiresAttention. Use this for 'soft delete' (status=archived); there is no hard delete via MCP in v1. Response: { productId, status }. Errors: { error: { code: 'not_found', ... } }.pim.productTypes.listList product types in a schema. ProductTypes are the flat product classification within a schema (NOT to be confused with categories, which are sales navigation). Response: { productTypes: [{ id, schemaId, code, labels, defaultTraitIds, parentCode, level, isActive, productCount }] }. Errors: { error: { code: 'not_found', ... } }.pim.traits.attributes.removeUnbind attribute definitions from a trait — clears their `traitId` only when it equals the given trait. Definitions bound to other traits are left untouched. Idempotent. Response: { traitId, attributeDefinitionIds, unbound: count, skipped: [{ id, reason }] }. Errors: { error: { code: 'not_found', ... } }.pim.channels.attributeConfig.setReplace the channel's per-attribute config overlay. Each entry binds an attribute definition to per-channel `isRequired`, `labelOverrides`, `exportKey`, and `sortOrder`. The channel's underlying schema must contain every referenced attribute definition; mismatches are skipped. Pass [] to clear the overlay (the channel falls back to the schema's defaults). Response: { channelId, applied: count, skipped: [{ attributeDefinitionId, reason }] }. Errors: { error: { code: 'not_found', ... } }.pim.products.searchSearch products in the current tenant. At most one filter dimension is honored at a time (in priority order: query > schemaId > productTypeId > categoryId > channelCode > traitId). If none are set, returns the tenant's products in default order. Pagination: 1-indexed `page` + `pageSize` (max 50). Response: { products: [{ id, schemaId, primaryTypeId, traitIds, categoryIds, channelCodes, skuList, productNumber, status, purpose, sourceSystem, sourceSystemProductId, updatedAt }], page, pageSize, totalCount, totalPages }. Errors: { error: { code: 'bad_input', ... } }.pim.attributeDefinitions.getGet an attribute definition by id, with full metadata (validation rules, allowed-value count, guidance text, unit conversion config). To enumerate the actual allowed values, call pim.attributeDefinitions.allowedValues.list (Phase 2). Response: { attributeDefinition: {...} }. Errors: { error: { code: 'not_found', ... } }.pim.productTypes.createCreate a product type in a schema, or return the existing one if `code` already exists in the schema. ProductType is the flat product classification within a schema (NOT category/sales nav). Response: { productType: {...}, created: bool }. Errors: { error: { code: 'not_found' | 'bad_input', ... } }.pim.schemas.set.defaultChannelsSet the schema's default channel codes — the codes auto-assigned to new products in this schema. This replaces the existing list. Pass [] to clear. Response: { schemaId, defaultChannelCodes }. Errors: { error: { code: 'not_found', ... } }.pim.schemas.listList PIM schemas in the current tenant. By default returns only tenant-owned, editable schemas (Master + Recipient). To inspect Import (file-import) or Standard (eClass/ETIM/UNSPSC) or Connector schemas, pass `kind` explicitly. Response: { schemas: [{ id, name, kind, contentLanguage, productTypeCount, traitCount, categoryCount, attributeDefinitionCount, isReadOnly, updatedAt }] }. Errors: { error: { code: 'bad_input', ... } }.pim.channels.createCreate a channel for the current tenant, or return the existing one if `code` already exists. Channels are thin distribution-target overlays on a master schema. To customize per-channel attribute requirements after creation, use pim.channels.attributeConfig.set. Response: { channel: {...}, created: bool }. Errors: { error: { code: 'not_found' | 'bad_input', ... } }.imports.smartImports.reviewReplace the SmartImport's inferred schema with the agent's adjusted version, transitioning the import to Reviewed. The schema shape is the same one returned in imports.smartImports.get's `inferredSchema` field — typically the agent fetches it, edits names/codes/grouping in its own context, and submits the result here. Response: { smartImport: {...} }. Errors: { error: { code: 'not_found' | 'bad_input', ... } }.imports.smartImports.runInferenceEnqueue the AI inference job for a SmartImport. The SmartImport must be in Analyzed state; transitions to Inferring. Inference runs as a background job — poll imports.smartImports.get periodically until status becomes Inferred (or Failed). Response: { smartImport: {...} }. Errors: { error: { code: 'not_found' | 'unprocessable', ... } }.pim.attributeDefinitions.aliases.setReplace the attribute definition's alias list. Aliases are alternate names used during import matching (e.g. 'Gewicht' as a German alias for 'weight'). Pass [] to clear. Response: { attributeDefinitionId, aliases }. Errors: { error: { code: 'not_found', ... } }.pim.products.getGet a single product with its variants and resolved attribute values. Use the optional `fields` whitelist to skip heavy sections when payload size matters. Allowed `fields` values: product, attributes, variants, channelStatuses. Attribute values are returned in their native JSON shape (string/number/bool/{lang:value}/["..."]) matching the PimValue wire format used by the HTTP API. Each attribute carries its definition code + dataType so the agent can interpret the value without a separate lookup. Response: { product?: {...}, attributes?: [{ attributeDefinitionId, code, dataType, scope, unit, value, updatedAt }], variants?: [{ id, sku, name, ean, gtin, attributes: [...] }], channelStatuses?: [...] }. Errors: { error: { code: 'not_found', ... } }.pim.attributeDefinitions.listList attribute definitions in a schema. Pass `traitId` to scope to a single trait, or pass `includeGlobalOnly=true` to get only global (non-trait-scoped) attributes. Response: { attributeDefinitions: [{ id, schemaId, traitId, code, labels, dataType, unit, isRequired, isTranslatable, scope, role, hasAllowedValues, allowedValueCount }] }. Errors: { error: { code: 'not_found', ... } }.pim.traits.listList traits in a schema. Traits are reusable groups of attribute definitions; products opt in to traits to gain their attributes. Response: { traits: [{ id, schemaId, code, labels, sortOrder, isActive }] }. Errors: { error: { code: 'not_found', ... } }.pim.channels.listList channels (distribution targets) for the current tenant. Optionally scope to a single schema. Response: { channels: [{ id, code, labels, country, schemaId, minReadinessPercent, tags, isActive, attributeConfigCount }] }. Errors: { error: { code: 'bad_input', ... } }.pim.products.archiveSoft-archive a product. Equivalent to pim.products.status.set with status=archived. Response: { productId, status: 'archived' }. Errors: { error: { code: 'not_found', ... } }.pim.schemas.set.defaultAiRoleSet the schema's default LLM role/context (the system-prompt-style preamble used in AI ops). Pass null to clear. Response: { schemaId, defaultAiRole }. Errors: { error: { code: 'not_found' | 'bad_input', ... } }.imports.smartImports.executeFinalize a SmartImport: create the PIM schema, traits, attribute definitions, products, variants, and attribute values. Synchronous — may take seconds for large imports. The SmartImport must be in Reviewed state. Transitions to Complete (or Failed on error). Response: { smartImport: {...} } with createdSchemaId, importedProductCount, importedVariantCount populated. Errors: { error: { code: 'not_found' | 'unprocessable', ... } }.pim.products.createCreate or upsert a single product by (sourceSystem, externalId). Re-running the same call returns the same productId and updates the existing record — never creates a duplicate. For ad-hoc creates with no upstream system, pass sourceSystem='mcp' (or another stable string the agent owns) so subsequent runs from the same agent stay idempotent. Response: { productId, sourceSystem, externalId, action: 'created'|'updated' }. Errors: { error: { code: 'not_found' | 'bad_input', ... } }.pim.schemas.createCreate a tenant-owned PIM schema, or return the existing one if `name` already exists for this tenant. Only Master and Recipient kinds are creatable via MCP — Standard (eClass/ETIM/UNSPSC) and Connector schemas are platform-managed. Response: { schema: {...}, created: bool }. Errors: { error: { code: 'bad_input', ... } }.pim.traits.attributes.addBind one or more attribute definitions to a trait by setting their `traitId`. Each attribute definition in the list has its `traitId` updated to the given trait. Idempotent — already-bound definitions are no-ops. Definitions in other schemas or other tenants are skipped with a reason. Response: { traitId, attributeDefinitionIds, bound: count, skipped: [{ id, reason }] }. Errors: { error: { code: 'not_found', ... } }.imports.smartImports.getGet a SmartImport by id. By default returns the metadata + field inventory + inferred schema (when present); the raw products list is omitted unless explicitly requested since it can be large. Response: { smartImport: {...}, fieldInventory?, inferredSchema?, rawProductCount, errorMessage? }. Errors: { error: { code: 'not_found', ... } }.
Last checked May 26, 2026
Screenshot
[ features ]
Product Data Management
Core capabilities for managing product data including import, validation, versioning, and localization.
Ability to import and export large volumes of product data via CSV, Excel, or other formats.
Configurable rules to validate product data quality and completeness.
Track changes to product data over time with ability to rollback.
Support for product variants with inherited attributes from parent products.
Multi-language support for product content.
How many languages are supported out of the box.
Supplier Portal
Capabilities for supplier onboarding, data collection, and collaboration.
Portal where suppliers can directly update their product information.
Structured process for onboarding new suppliers with approvals.
Automated scoring of supplier-provided data quality.
Analytics on supplier data quality and response times.
White-label supplier portal with custom branding.
Compliance & Security
Security certifications, compliance features, and access control capabilities.
SOC 2 Type I or Type II certification.
ISO 27001 information security certification.
Built-in tools for GDPR compliance (data export, deletion, consent).
Complete audit log of all data changes.
Granular permissions based on user roles.
Single Sign-On integration support.
eCommerce Integrations
Connectivity with other systems including DAM, ERP, e-commerce platforms, and marketplaces.
Integration with Digital Asset Management systems.
Native integrations with ERP systems.
Pre-built connectors for e-commerce platforms.
Direct syndication to online marketplaces.
Type of API available for custom integrations.
Outbound webhooks for event-driven integrations.
Listing AI & Automation
Artificial intelligence and automation capabilities for content generation, categorization, and optimization.
AI-powered generation of product descriptions and content.
AI tools for image optimization, background removal, etc.
AI-powered automatic product categorization.
Automatic extraction of product attributes from descriptions or images.
Built-in machine translation for product content.
eCommerce Publishing & Channels
Capabilities for distributing product content across multiple channels and formats.
Publish product data to multiple channels from a single source.
Generate print-ready catalogs and datasheets.
API-first architecture for headless commerce.
Analytics on product performance across digital channels.
Schedule product updates to go live at specific times.
Compare With
Reviews
No reviews yet. Be the first to review FacetFlux!