FacetFlux
Verified verified 3 jul 2026The Particle Accelerator for Product Data
TL;DR
FacetFlux is an API-first, headless PIM designed for mid-market e-commerce brands ($20M-$200M GMV) navigating rapid scaling. It differentiates itself with a graph-based architecture and an AI-powered 'Dirty Data' staging layer that cleanses messy supplier imports before they reach the core system.
What Users Actually Pay
No user-reported pricing yet.
Our Take
FacetFlux positions itself as a 'Particle Accelerator' for product data, prioritizing technical agility and speed over the heavy administrative workflows of legacy PIMs. Its graph-based schema allows for complex product relationships and rapid attribute changes that would cripple traditional relational databases, making it an ideal choice for brands in high-volatility categories. The platform's standout feature is the 200ms synchronization engine, which ensures that catalog updates across Shopify, Mirakl, and other headless storefronts happen in near real-time. While it excels in performance and AI-assisted ingestion, its focus on the 'scale-up' niche means it may lack the extensive legacy ERP connector libraries found in enterprise giants like Akeneo or Salsify. It is best suited for digitally native brands that have outgrown spreadsheets but require a developer-centric, headless approach to product information.
Pros
- + Blazing fast 200ms synchronization for real-time inventory and data updates
- + AI-driven 'Dirty Data' staging layer handles raw CSV and PDF imports without manual cleaning
- + Graph-based database architecture supports complex, non-linear product relationships
- + Modern spreadsheet-like interface allows for high-performance bulk editing and macros
Cons
- - Targeting $20M+ GMV brands creates a high entry barrier for smaller retailers
- - Relatively new platform (founded 2024) with a smaller ecosystem of community plugins
- - Focused on headless architecture, which may require more developer resources to implement
Sentiment Analysis
Sentiment has remained stable since last capture. Sentiment has improved from 0.83 to 0.88 as the platform matures. Users highly value the speed of the graph architecture and the AI staging layer, frequently comparing its developer experience to modern tools like Linear.
Sentiment Over Time
By Source
15 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."
32 mentions
Sample quotes (1)
- "The 'Dirty Data' staging is a game changer for supplier onboarding. We used to spend days in Excel before importing; now AI does the heavy lifting."
24 mentions
Sample quotes (1)
- "Finally a PIM that treats product data as a typed graph rather than just documents. FacetFlux is the future of headless commerce."
Agent Readiness
59/100FacetFlux is highly agent-ready due to its API-first, headless nature and 'Dirty Data' staging layer, which is purpose-built for AI-assisted data manipulation. Its support for REST and GraphQL, combined with robust webhook triggers and clear OpenAPI documentation, makes it an ideal hub for autonomous agents to manage product catalogs, though the lack of no-code connectors like Zapier means integration currently requires developer intervention.
Last checked Jun 17, 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 Jul 10, 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!