Helium PIM API

Generic UGC

Queue one Shopify product into a UGC reel. Poll until videoUrl is an HTTPS mp4.

One product from a Shopify URL → workspace Generic UGC Video → existing type generic_ugc_video → ugc_video.* DAG → 9:16 reel.

Do not pass workspaceId. Do not call columns.author or resources.create_type. Secret org key only (pim_sk_…, preset org_admin or workspace_admin).

This is not Brand UGC. Brand UGC syncs a whole {slug}_products catalog in Brand Reachout 1. This API inserts one row on the shared type.

  • Prod: https://intelligence-backend.gethelium.co
  • Local: http://localhost:3001
  • Catalog: GET /v1/catalog/generic_ugc
  • HTTP only. Not on MCP. Skill pim/recipes/generic-ugc.

runId is the new resource id. There is no separate run table.

1. Queue

curl -sS https://intelligence-backend.gethelium.co/v1/generic_ugc \
  -H "Authorization: Bearer pim_sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "op": "run",
    "url": "https://www.wforwoman.com"
  }'

A product-page URL (https://www.wforwoman.com/products/some-handle) fetches that SKU via /products/{handle}.json. Any other URL takes the last product from /products.json?limit=10.

{
  "ok": true,
  "module": "generic_ugc",
  "op": "run",
  "data": {
    "id": "ab7b6f5d-42a7-4c9c-9aea-750a7772db61",
    "status": "queued",
    "phase": "generating",
    "workspaceId": "cb2a33f4-e96f-4871-8950-904d6296cf82",
    "hostname": "wforwoman.com",
    "resourceTypeKey": "generic_ugc_video",
    "resourceId": "ab7b6f5d-42a7-4c9c-9aea-750a7772db61",
    "title": "Blue Floral Printed Modal Straight Kurta with Pants Co-ord Set"
  }
}

HTTP returns immediately. Save data.id — that is runId for get. Each run creates a new row.

2. Poll

curl -sS https://intelligence-backend.gethelium.co/v1/generic_ugc \
  -H "Authorization: Bearer pim_sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "op": "get",
    "runId": "ab7b6f5d-42a7-4c9c-9aea-750a7772db61"
  }'

Stop when data.status is succeeded or failed. Stills ~1–2 min; video ~5–15 min (generate timeout ~40m). Poll every 8s.

RUN_ID="ab7b6f5d-42a7-4c9c-9aea-750a7772db61"

while true; do
  BODY=$(curl -sS https://intelligence-backend.gethelium.co/v1/generic_ugc \
    -H "Authorization: Bearer pim_sk_live_..." \
    -H "Content-Type: application/json" \
    -d "{\"op\":\"get\",\"runId\":\"$RUN_ID\"}")
  STATUS=$(printf '%s' "$BODY" | jq -r '.data.status')
  printf '%s' "$BODY" | jq '{status: .data.status, phase: .data.phase, videoUrl: .data.videoUrl, error: .data.error}'
  case "$STATUS" in succeeded|failed) printf '%s' "$BODY" | jq '.data'; break ;; esac
  sleep 8
done

3. Get the video

When data.status is succeeded, data.videoUrl is an HTTPS mp4 on assets.gethelium.co. Play or download that URL.

{
  "ok": true,
  "module": "generic_ugc",
  "op": "get",
  "data": {
    "id": "ab7b6f5d-42a7-4c9c-9aea-750a7772db61",
    "status": "succeeded",
    "phase": "succeeded",
    "videoUrl": "https://assets.gethelium.co/workspaces/cb2a33f4-e96f-4871-8950-904d6296cf82/generative/.../0-….mp4",
    "cells": {
      "ugc_video.video": { "state": "fresh", "hasValue": true }
    },
    "error": null
  }
}

videoUrl is projected on read (AssetRefs stay in Postgres). Grid POST /v1/resources op=grid on the same type also returns cells["ugc_video.video"].value.src.