Helium PIM API

Brand UGC

Queue a Shopify brand UGC reel with curl. Poll until the video cell is fresh.

Hardcoded pipeline: brand name + Shopify storefront URL → workspace Brand Reachout 1 → {slug}_products → first synced product → ugc_video.* DAG → 9:16 reel.

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

  • Prod: https://intelligence-backend.gethelium.co
  • Local: http://localhost:3001
  • Catalog: GET /v1/catalog/ugc
  • MCP: pim_ugc — same op + body. Skill pim/recipes/brand-ugc.

runId is the {slug}_products resource type id. There is no separate run table.

1. Queue

curl -sS https://intelligence-backend.gethelium.co/v1/ugc \
  -H "Authorization: Bearer pim_sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "op": "run",
    "brandName": "Asopalav",
    "url": "https://asopalav.com"
  }'
{
  "ok": true,
  "module": "ugc",
  "op": "run",
  "data": {
    "id": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
    "status": "queued",
    "phase": "syncing",
    "workspaceId": "11111111-2222-4333-8444-555555555555",
    "hostname": "asopalav.com",
    "resourceTypeKey": "asopalav_products"
  }
}

HTTP returns immediately. A second run for the same brand reuses that type (and any in-flight Shopify sync); status may already be "running".

Save data.id — that is runId for get.

2. Poll

curl -sS https://intelligence-backend.gethelium.co/v1/ugc \
  -H "Authorization: Bearer pim_sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "op": "get",
    "runId": "a1b2c3d4-e5f6-4789-a012-3456789abcde"
  }'

Stop when data.status is succeeded or failed. Sync + video is typically 15–30+ minutes (sync timeout ~15m, generate ~40m). Poll every 8s.

RUN_ID="a1b2c3d4-e5f6-4789-a012-3456789abcde"

while true; do
  BODY=$(curl -sS https://intelligence-backend.gethelium.co/v1/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, product: .data.product, error: .data.error}'
  case "$STATUS" in succeeded|failed) printf '%s' "$BODY" | jq '.data'; break ;; esac
  sleep 8
done

In progress (generating)

{
  "ok": true,
  "module": "ugc",
  "op": "get",
  "data": {
    "id": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
    "status": "running",
    "phase": "generating",
    "workspaceId": "11111111-2222-4333-8444-555555555555",
    "brandName": "Asopalav",
    "url": "https://asopalav.com",
    "hostname": "asopalav.com",
    "resourceTypeKey": "asopalav_products",
    "resourceTypeId": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
    "connectorInstanceId": "c0c0c0c0-1111-4222-8333-444444444444",
    "syncRunId": "d1d1d1d1-2222-4333-8444-555555555555",
    "resourceId": "bbbbbbbb-cccc-4ddd-8eee-ffffffffffff",
    "product": {
      "id": "bbbbbbbb-cccc-4ddd-8eee-ffffffffffff",
      "title": "Banarasi Silk Saree"
    },
    "columnIds": {
      "ugc_video.audience": "01010101-0000-4000-8000-000000000001",
      "ugc_video.vibe": "01010101-0000-4000-8000-000000000002",
      "ugc_video.concept": "01010101-0000-4000-8000-000000000003",
      "ugc_video.script": "01010101-0000-4000-8000-000000000004",
      "ugc_video.product_stills": "01010101-0000-4000-8000-000000000005",
      "ugc_video.avatar_description": "01010101-0000-4000-8000-000000000006",
      "ugc_video.avatar_stills": "01010101-0000-4000-8000-000000000007",
      "ugc_video.environment_description": "01010101-0000-4000-8000-000000000008",
      "ugc_video.environment_stills": "01010101-0000-4000-8000-000000000009",
      "ugc_video.video": "01010101-0000-4000-8000-00000000000a"
    },
    "cells": {
      "ugc_video.audience": { "state": "fresh", "hasValue": true },
      "ugc_video.vibe": { "state": "fresh", "hasValue": true },
      "ugc_video.concept": { "state": "fresh", "hasValue": true },
      "ugc_video.script": { "state": "pending", "hasValue": false },
      "ugc_video.product_stills": { "state": "fresh", "hasValue": true },
      "ugc_video.avatar_description": { "state": "idle", "hasValue": false },
      "ugc_video.avatar_stills": { "state": "idle", "hasValue": false },
      "ugc_video.environment_description": { "state": "idle", "hasValue": false },
      "ugc_video.environment_stills": { "state": "idle", "hasValue": false },
      "ugc_video.video": { "state": "idle", "hasValue": false }
    },
    "error": null,
    "createdAt": "2026-09-02T11:00:00.000Z",
    "updatedAt": "2026-09-02T11:12:00.000Z",
    "finishedAt": null
  }
}

Success is ugc_video.video fresh with a value — not merely status: "running".

Succeeded

{
  "ok": true,
  "module": "ugc",
  "op": "get",
  "data": {
    "id": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
    "status": "succeeded",
    "phase": "succeeded",
    "workspaceId": "11111111-2222-4333-8444-555555555555",
    "brandName": "Asopalav",
    "url": "https://asopalav.com",
    "hostname": "asopalav.com",
    "resourceTypeKey": "asopalav_products",
    "resourceTypeId": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
    "connectorInstanceId": "c0c0c0c0-1111-4222-8333-444444444444",
    "syncRunId": "d1d1d1d1-2222-4333-8444-555555555555",
    "resourceId": "bbbbbbbb-cccc-4ddd-8eee-ffffffffffff",
    "product": {
      "id": "bbbbbbbb-cccc-4ddd-8eee-ffffffffffff",
      "title": "Banarasi Silk Saree"
    },
    "columnIds": {
      "ugc_video.audience": "01010101-0000-4000-8000-000000000001",
      "ugc_video.vibe": "01010101-0000-4000-8000-000000000002",
      "ugc_video.concept": "01010101-0000-4000-8000-000000000003",
      "ugc_video.script": "01010101-0000-4000-8000-000000000004",
      "ugc_video.product_stills": "01010101-0000-4000-8000-000000000005",
      "ugc_video.avatar_description": "01010101-0000-4000-8000-000000000006",
      "ugc_video.avatar_stills": "01010101-0000-4000-8000-000000000007",
      "ugc_video.environment_description": "01010101-0000-4000-8000-000000000008",
      "ugc_video.environment_stills": "01010101-0000-4000-8000-000000000009",
      "ugc_video.video": "01010101-0000-4000-8000-00000000000a"
    },
    "cells": {
      "ugc_video.audience": { "state": "fresh", "hasValue": true },
      "ugc_video.vibe": { "state": "fresh", "hasValue": true },
      "ugc_video.concept": { "state": "fresh", "hasValue": true },
      "ugc_video.script": { "state": "fresh", "hasValue": true },
      "ugc_video.product_stills": { "state": "fresh", "hasValue": true },
      "ugc_video.avatar_description": { "state": "fresh", "hasValue": true },
      "ugc_video.avatar_stills": { "state": "fresh", "hasValue": true },
      "ugc_video.environment_description": { "state": "fresh", "hasValue": true },
      "ugc_video.environment_stills": { "state": "fresh", "hasValue": true },
      "ugc_video.video": { "state": "fresh", "hasValue": true }
    },
    "error": null,
    "createdAt": "2026-09-02T11:00:00.000Z",
    "updatedAt": "2026-09-02T11:28:00.000Z",
    "finishedAt": "2026-09-02T11:28:00.000Z"
  }
}

The reel is the ugc_video.video cell on data.product.id in Brand Reachout 1. Fetch it with POST /v1/cells { "op": "get", "workspaceId", "resourceId", "columnDefinitionId": data.columnIds["ugc_video.video"] }.

Failed (HTTP 200)

A finished failure is still ok: true. Read data.error.

{
  "ok": true,
  "module": "ugc",
  "op": "get",
  "data": {
    "id": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
    "status": "failed",
    "phase": "failed",
    "workspaceId": "11111111-2222-4333-8444-555555555555",
    "brandName": "Asopalav",
    "url": "https://asopalav.com",
    "hostname": "asopalav.com",
    "resourceTypeKey": "asopalav_products",
    "resourceTypeId": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
    "connectorInstanceId": "c0c0c0c0-1111-4222-8333-444444444444",
    "syncRunId": "d1d1d1d1-2222-4333-8444-555555555555",
    "resourceId": null,
    "product": null,
    "columnIds": {},
    "cells": {},
    "error": "Shopify sync finished with no products",
    "createdAt": "2026-09-02T11:00:00.000Z",
    "updatedAt": "2026-09-02T11:04:00.000Z",
    "finishedAt": "2026-09-02T11:04:00.000Z"
  }
}

Other error strings: Shopify sync failed / timed out, UGC generate timed out, Column ugc_video.script failed.

Errors (HTTP)

403 — publishable, generate_only, or resource_writer on run. get is read.

{
  "ok": false,
  "error": {
    "code": "forbidden",
    "message": "Key cannot ugc.run",
    "module": "ugc",
    "op": "run"
  }
}

404 — org has no workspace named exactly Brand Reachout 1 (never auto-created), or runId is not that workspace's product type.

{
  "ok": false,
  "error": {
    "code": "not_found",
    "message": "Workspace \"Brand Reachout 1\" was not found in this organisation",
    "module": "ugc",
    "op": "run"
  }
}

What run kicks

  1. Resolve Brand Reachout 1 in the key's org.
  2. Ensure type {slug}_products (≤48 chars, e.g. asopalav_products). Never SYSTEM product.
  3. Ensure shopify_public for that hostname + type; sync create_all_new.
  4. Publish ugc_video.* if not already live.
  5. Generate the first active product only (resourceIds: [thatId], onlyMissing: true).
KeyOutputDepends on
ugc_video.audiencetext—
ugc_video.vibetextaudience
ugc_video.concepttextaudience, vibe
ugc_video.scripttextaudience, vibe, concept
ugc_video.product_stillsimage multirow images
ugc_video.avatar_descriptiontextaudience, concept, script
ugc_video.avatar_stillsimage multiavatar_description
ugc_video.environment_descriptiontextaudience, vibe, concept, script
ugc_video.environment_stillsimage multienvironment_description
ugc_video.videovideoscript, vibe, three stills

Video model: alibaba/wan-3.0/reference-to-video (9:16, 15s, 720p, audio). Out of scope: ElevenLabs, multi-model video A/B, non-Shopify, caller-chosen workspace.

Statuses: queued · running · succeeded · failed.
Phases: provision · syncing · ensuring_columns · generating · succeeded · failed.