Assortment Publish Integration¶
Overview¶
This document outlines the architecture for integrating external systems with VibeIQ's Assortment Publish functionality. When users publish changes from a Plan, the system generates an AssortmentPublishChange event that downstream systems can consume to synchronize data.
Authentication & API Access¶
Authenticating with VibeIQ¶
VibeIQ provides three programmatic access patterns:
- JavaScript/TypeScript SDK - @contrail/sdk
- Java SDK
- HTTPS REST API
Generating an API Key¶
To authenticate, you'll need an API key. Generate one using the Contrail CLI:
contrail app getApiKey
This generates a key in the format: app:uEL7GCBZ7Xhd4w2z
Note
VibeIQ can provide an access token directly if using the CLI is not preferred.
Authentication Examples¶
import { login } from '@contrail/sdk';
await login({
apiKey: 'app:uEL7GCBZ7Xhd4w2z',
orgSlug: 'your-org-slug'
});
After successful authentication, all SDK functions requiring authentication will work automatically.
curl --location 'https://api.vibeiq.com/prod/api/items/:itemId' \
--header 'X-Api-Key: app:uEL7GCBZ7Xhd4w2z' \
--header 'X-Api-Org: your-org-slug'
For HTTPS REST requests, pass the API key via the X-Api-Key header and organization slug via X-Api-Org header.
Authenticating from External Systems¶
If your integration requires VibeIQ to call your external system, you should provide:
- API endpoint URL for receiving data
- Preferred authentication scheme (e.g., Personal Access Tokens, API keys)
- Any IP allowlisting requirements if needed
Understanding Assortment Publishes¶
Publish Workflow¶
- Users create and modify Items (Products, Materials, etc.) in VibeIQ Plans
- Users adopt Images, Colors, and other attributes onto these Items
- To persist changes to send them to downstream systems, users publish the Plan
- The system saves Plan rows to the underlying Assortment
- An
AssortmentPublishChangeobject is created as a snapshot of those changes - External systems can consume this change data
Key Concepts¶
Item¶
A global object representing a Product Family or Product Option (Colorway). Properties are global and apply everywhere in VibeIQ.
They are typically referred to by other *Item types that indicate tighter levels of seasonal or assortment-level scopes.
- Roles determine the type:
roles: ['family']- Family-level Itemroles: ['option']- Option-level Item
- Unique identifier:
id(string)- Example: Item
abc= Itemabc
- Example: Item
ProjectItem¶
A seasonal object representing an Item within a specific Project (Season).
- Unique identifier:
id- concatenation ofProject.id:Item.id- Example: Project
123+ Itemabc= ProjectItem123:abc - Properties are seasonal and apply only within that
Project
- Example: Project
AssortmentItem¶
A seasonal object representing an Item within a specific Assortment.
- Unique identifier:
id- concatenation ofAssortment.id:Item.id- Example: Assortment
789+ Itemxyz= AssortmentItem789:xyz - Properties are applicable only to this
Assortment, even if there are multiple Assortments in the same Project, and the sameItemlives in thoseAssortments.
- Example: Assortment
Publish Events¶
AssortmentPublish Event¶
When a Plan is published, VibeIQ generates an assortment|publish event:
interface AssortmentPublish {
assortmentId: string;
assortmentPublishChangeId: string;
}
Fields:
assortmentId- Identifier of the published AssortmentassortmentPublishChangeId- Identifier of theAssortmentPublishChangeobject containing detailed a detailed snapshot of the publish data
AssortmentPublishChange Object¶
interface AssortmentPublishChange {
id: string;
orgId?: string;
assortmentId?: string;
detailDownloadLink?: string;
detail?: AssortmentPublishChangeDetail;
assortmentBaselineDownloadLink?: string;
deleteDataDownloadLink?: string;
hydratedDetailDownloadLink?: string;
createdOn?: Date;
updatedOn?: Date;
createdById?: string;
updatedById?: string;
}
Key Properties:¶
| Property | Structure | Description |
|---|---|---|
assortmentBaselineDownloadLink |
|
An AWS S3 URL pointing to a JSON file containing the Assortment's complete state at publish time.
This URL expires after 24 hours. Retrieving the AssortmentPublishChange object again with another API call will refresh it.
Example request: curl |
deleteDataDownloadLink |
|
An S3 URL pointing to a JSON file with the same structure as the baseline, but containing only Items that were dropped or deleted in this specific publish.
⚠️ Warning: Delete data does NOT accumulate across publishes. Each AssortmentPublishChange only contains deletes from that individual publish.
|
detail |
|
Contains granular change arrays:
ℹ️ Note: If arrays are too large, [name]DownloadURL properties will be provided instead to avoid excessive payload size.
|
Integration Patterns¶
Pattern 1: Event-Driven (Push)¶
VibeIQ pushes publish events to your external system in real-time.
Setup¶
- Provide an API endpoint to receive
AssortmentPublishevents - Configure an Event Workflow in VibeIQ to forward events to your endpoint
- Your system receives the event and calls back to VibeIQ for full change data
Data Flow¶
Implementation¶
Step 1: Receive the event
Your endpoint receives:
{
"assortmentId": "25uDvGNpYhZHw2nt",
"assortmentPublishChangeId": "lTuARQLrhgWHNr3k"
}
Step 2: Retrieve full change data
You then go retrieve the AssortmentPublishChange object by its assortmentId and assortmentPublishChangeId.
curl --location 'https://api.vibeiq.com/prod/api/assortments/25uDvGNpYhZHw2nt/history/lTuARQLrhgWHNr3k' \
--header 'X-Api-Key: your-api-key' \
--header 'X-Api-Org: your-org-slug'
import { Entities } from '@contrail/sdk';
const changeHistory = await new Entities().get({
entityName: 'assortment',
id: '25uDvGNpYhZHw2nt',
suffix: 'history/lTuARQLrhgWHNr3k'
});
Step 3: Download baseline and process
Process the response to retrieve the publish data as described in the AssortmentPublishChange Object section.
// Get the baseline data
const baselineUrl = changeHistory.assortmentBaselineDownloadLink;
const baselineResponse = await fetch(baselineUrl, {
headers: {
'X-Api-Key': 'your-api-key',
'X-Api-Org': 'your-org-slug'
}
});
const baselineData = await baselineResponse.json();
// Process assortment items
for (const assortmentItem of baselineData.assortmentItems) {
const item = assortmentItem.item;
const itemFamily = assortmentItem.item.itemFamily;
const projectItem = assortmentItem.projectItem;
// Map to your system's data model
// ...
}
Pattern 2: Polling (Pull)¶
Your external system periodically checks for new publishes. This makes an assumption that you have kept a record of all Assortment IDs that you'd like to track.
You can also call the GET /assortments API and paginate through the response to retrieve all Assortment IDs in your org.
Setup¶
- Maintain a registry of VibeIQ Assortment IDs in your system, or call the
GET /assortmentsAPI to retrieve all IDs in your org. - Store the timestamp or ID of the last processed publish, or always just grab the latest one.
- Periodically poll the history API for new publishes on each
AssortmentID.
Data Flow¶
Implementation¶
Step 1: Poll for publishes
curl --location 'https://api.vibeiq.com/prod/api/assortments/:assortmentId/history' \
--header 'X-Api-Key: your-api-key' \
--header 'X-Api-Org: your-org-slug'
import { Entities } from '@contrail/sdk';
const history = await new Entities().get({
entityName: 'assortment',
id: assortmentId,
suffix: 'history'
});
This returns an array of all AssortmentPublishChange objects for the Assortment.
Step 2: Filter for new publishes
const history = await fetch(
`https://api.vibeiq.com/prod/api/assortments/${assortmentId}/history`,
{
headers: {
'X-Api-Key': apiKey,
'X-Api-Org': orgSlug
}
}
).then(r => r.json());
// Filter for publishes after last processed timestamp
const lastProcessedTime = getLastProcessedTimestamp(assortmentId);
const newPublishes = history.filter(
change => new Date(change.createdOn) > lastProcessedTime
);
// Process each new publish
for (const change of newPublishes) {
await processPublishChange(assortmentId, change.id);
updateLastProcessedTimestamp(assortmentId, change.createdOn);
}
Step 3: Process and ingest data from each publish
Reference the Publish Events section to extract the data you need from the resulting payload.