Skip to content

Federated Load From File

Introduction

The POST /Content/federatedLoad/fromFile endpoint provides a simplified approach to loading images onto Items in VibeIQ. Instead of the multi-step process of creating a File, creating Content, linking it to an entity, and then assigning the primary image separately, this endpoint handles Content creation, entity linking, and primary image assignment in a single call.

Covered Concepts

  • Uploading temporary files to VibeIQ.
  • Using the federated load endpoint to create Content, link it to an Item, and optionally set it as the primary image — all in one request.

End Product of this Tutorial

  • Images will be uploaded and linked to Items via Content.
  • Primary images will be assigned to Items automatically based on the isPrimary flag.

How It Compares to the Standard Approach

The standard programmatic image loading tutorial requires three separate steps:

  1. Create a File and upload the image data.
  2. Create a Content entity linking the File to an Item or Color.
  3. Update the Item or Color to set primaryViewableId.

The federated load endpoint simplifies this to two steps:

  1. Upload a temporary file.
  2. Call /Content/federatedLoad/fromFile — which creates the Content, links it to the owner, and handles primary image assignment.

Prerequisites: Target Entities Must Have a Federated ID

The federated load endpoint resolves the target entity using its federatedId. This means the Item (or other entity) you are loading an image onto must already have a federatedId value set. If the entity does not have a federated ID, the API cannot locate it and will return a 400 Bad Request error:

{
  "statusCode": 400,
  "timestamp": "2026-02-18T12:00:00.000Z",
  "message": "Item object cannot be found for federated id ITEM-001."
}

Authentication

App Authentication

If your image load is running as part of an app action, you do not need to explicitly authenticate, and can skip this section. App actions are authorized automatically during runtime.

For detailed information on authenticating with Contrail, including how to generate API keys and use them with the SDK, see the Authentication & API Access guide.

Step 1: Upload Temporary Files

First, upload each image as a temporary file. Temporary files use a ttl (time-to-live) instead of an ownedByReference, meaning they will be automatically cleaned up after the specified duration. The federated load endpoint will handle permanent file creation and ownership.

federated_image_load_script.ts
import {login, Files, Entities} from '@contrail/sdk';
import * as fs from "fs/promises"

const pLimit = require('p-limit');
const limit = pLimit(10);

const loginCredentials = {
  orgSlug: process.env.USER_ORG,
  apiKey: process.env.API_KEY,
};

async function loadImages() {
    await login(loginCredentials);

    const contentType = "image/jpeg";
    const imagesToUpload = await fs.readdir('./images');
    let uploadedFiles = [];

    const uploadOperations = imagesToUpload.map((fileName) =>
        limit(async () => {
            try {
                const arrayBuffer = await fs.readFile(`./images/${fileName}`);
                const imageBuffer = Buffer.from(new Uint8Array(arrayBuffer));

                const createdFile = await new Files().createAndUploadFileFromBuffer(
                    imageBuffer,
                    contentType,
                    fileName,
                    null,
                    24 * 60 * 60 * 1000 // 24-hour TTL
                );

                uploadedFiles.push({
                    fileId: createdFile.id,
                    fileName,
                });
            } catch (e) {
                console.log(`Failed to upload image: ${fileName}`, e);
            }
        })
    );

    await Promise.all(uploadOperations);
}

Upload a temporary file using the same two-step process as the standard approach — request pre-signed upload information, then upload the file to S3. The key difference is that you provide a ttl instead of an ownedByReference:

curl -X POST "https://api.vibeiq.com/prod/api/files" \
  -H "x-api-key: $API_KEY" \
  -H "x-api-org: $USER_ORG" \
  -H "Content-Type: application/json" \
  -d '{
    "contentType": "image/jpeg",
    "fileName": "image.jpeg",
    "ttl": 86400000
  }'

Then upload the file to S3 using the uploadPost data from the response (see Step 1 of the standard approach for the full S3 upload details).

Note the id field from the response — you will need it in the next step.

Step 2: Federated Load Content From File

With the temporary files uploaded, use the /Content/federatedLoad/fromFile endpoint to create Content, link it to the target Item, and optionally set it as the primary image.

Request Parameters

Parameter Type Required Description
federatedId string Yes The federated ID of the target Item.
fileId string Yes The ID of the temporary file uploaded in Step 1.
isPrimary boolean No Set to true to designate this image as the Item's primary image. Defaults to false.
fileName string No Override the file name. If not provided, the original file name is used.
ownerType string No The type of entity that owns the content. Defaults to item.

Automatic Primary Assignment

If the target entity does not already have a primaryViewableId set, the image will automatically be assigned as the primary image regardless of the isPrimary flag.

federated_image_load_script.ts
const loadOperations = uploadedFiles.map((file) =>
    limit(async () => {
        try {
            const itemFederatedId = file.fileName.split("_")[0];

            const result = await new Entities().create({
                entityName: 'content',
                suffix: 'federatedLoad/fromFile',
                object: {
                    federatedId: itemFederatedId,
                    fileId: file.fileId,
                    isPrimary: true,
                },
            });

            console.log(`Loaded image for ${itemFederatedId}: ${result.id}`);
        } catch (e) {
            console.log(`Failed to load image for ${file.fileName}`, e);
        }
    })
);

await Promise.all(loadOperations);
curl -X POST 'https://api.vibeiq.com/prod/api/content/federatedLoad/fromFile' \
  -H 'x-api-key: $API_TOKEN' \
  -H 'x-api-org: $USER_ORG' \
  -H 'Content-Type: application/json' \
  -d '{
  "federatedId": "ITEM-001",
  "fileId": "fkS4cOA25o5IX",
  "isPrimary": true
}'

Some iterative method will be required to run this request for all items and images.

What the Endpoint Does

Behind the scenes, the /Content/federatedLoad/fromFile endpoint performs the following:

  1. Resolves the owner: Looks up the Item by its federatedId.
  2. Creates Content and File: Creates a new Content entity and a permanent File owned by that Content. The image data is copied from the temporary file.
  3. Links Content to the owner: Creates a ContentHolderContent association between the new Content and the Item.
  4. Handles primary image: If isPrimary is true or the Item has no existing primary image, sets the Item's primaryViewableId to the new Content's ID.
  5. Cleans up: Deletes the temporary file after processing.

Idempotent Behavior

The endpoint uses a federated ID derived from the owner and file name (item:{federatedId}:{fileName}) to identify content. If content with the same federated ID already exists and the file data hasn't changed (based on checksum), the existing Content is returned without creating a duplicate. If the file data has changed, the existing Content is updated with the new file.