> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omnicommerce.sg/llms.txt
> Use this file to discover all available pages before exploring further.

# Import and queue product enrichment

> Import selected Zalora products and queue AI enrichment over the developer API without an AOP.

Use these developer APIs from Windmill or another server. The ID arrays travel as ordinary JSON; no AOP or assistant model is needed to route them. AI enrichment itself still incurs organization-level AI usage.

| Operation                              | Endpoint                       | Bearer credential scope |
| -------------------------------------- | ------------------------------ | ----------------------- |
| Import selected Zalora product sets    | `POST /api/v1/zalora/import`   | `catalog:write`         |
| Read import state and Omni product IDs | `GET /api/v1/zalora/import`    | `catalog:read`          |
| Queue product enrichment               | `POST /api/v1/products/enrich` | `catalog:enrich`        |

Send `Authorization: Bearer $OMNI_API_KEY`. Organization ownership comes from the credential. For session authentication, include `organizationId` in the JSON body or status query.

The table lists the scopes for the API requests themselves. Enrichment also uses organization skills, research, and brand-kit tools in the background. A restricted key used for this workflow needs these additional scopes:

| Scope            | Used by enrichment                                                                 |
| ---------------- | ---------------------------------------------------------------------------------- |
| `mcp:tools:call` | Authorize the enrichment agent's tool calls; this does not require running an AOP. |
| `aops:read`      | Load full organization skill documents with `assistant_getSkill`.                  |
| `documents:read` | Search the organization's knowledge base.                                          |
| `branding:read`  | Read brand-kit guidelines.                                                         |
| `branding:write` | Resolve a brand kit, which can create one when needed.                             |

Keep `catalog:read` as well for product research and pricing context. A key with only `catalog:enrich` can queue a job, but its background tool calls can fail authorization. HTTP `202` confirms queue acceptance, not successful skill loading or completed enrichment. Skills use the organization attached to the API key; passing an organization ID does not add permissions.

## Import selected Zalora products

```bash theme={null}
curl --fail-with-body https://omnicommerce.sg/api/v1/zalora/import \
  -H "Authorization: Bearer $OMNI_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "productSetIds": ["1001", "1002"],
    "storeId": "zalora-store-id"
  }'
```

`productSetIds` are Zalora IDs. `storeId` is the connected Zalora store/account ID, not a token-record ID. You may omit the store when exactly one connected store or one default store is available.

The API accepts 1–25 entries, trims them, and removes duplicates while preserving order. HTTP `202` returns `jobId`, `links.job`, and `data` containing the normalized IDs, job correlations, and ordered per-target queue receipts. Queue acceptance does not establish import completion.

Follow the returned `links.job` URL unchanged with the same bearer credential. This performs one status read. Inspect `data.terminal` and `data.success`; the envelope's `status: "completed"` means only the status request finished. While the import is pending, check the same URL again after a delay. It contains the original target selection used to resolve product IDs; editing the URL does not change the import.

A completed successful result includes:

```json theme={null}
{
  "data": {
    "kind": "zalora_import",
    "status": "success",
    "terminal": true,
    "success": true,
    "productIds": ["omni-product-1", "omni-product-2"]
  }
}
```

Inspect `data.error` and `data.missingExternalIds` on failure. Partial imports are not reported as successful. Only one import may be active for a store; a competing request returns `409`. For 500 product-set IDs, process batches of at most 25, finishing each import before starting the next for that store.

## Queue enrichment and stop

If you already have Omni product IDs, call this endpoint directly. Otherwise, finish the import first and use its returned `data.productIds`. Import job IDs and Zalora product-set IDs are not Omni product IDs.

```bash theme={null}
curl --fail-with-body https://omnicommerce.sg/api/v1/products/enrich \
  -H "Authorization: Bearer $OMNI_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "productIds": ["omni-product-1", "omni-product-2"],
    "selectedFields": ["description"],
    "instructions": "Do not change the title. Do not add warranty or return information to the description.",
    "autoApplyHighConfidence": true,
    "enrichmentAutoApplyThreshold": 0.7
  }'
```

The API accepts up to 1,000 entries from the authenticated organization's catalog, regardless of marketplace. It trims and deduplicates IDs in requested order and uses the shared enrichment queue. It returns HTTP `202` once the batch has been queued. No enrichment wait or publishing call is required.

The response contains `data.productIds`, `data.jobIds`, `data.batchJobId`, `data.enrichmentBatchId`, ordered `data.results`, and `links.review`. `data.succeededCount` counts accepted targets, not completed enrichments. Save the receipt to track the request and open the Review link to inspect results later.

* `selectedFields: ["description"]` excludes the product name from application, preserving the title. The shared enrichment service always includes weight and dimensions. This endpoint does not queue gallery image or marketplace taxonomy workflows.
* `autoApplyHighConfidence` defaults to `false`. When `true`, qualifying proposals can update the Omni catalog automatically. `enrichmentAutoApplyThreshold` accepts 0.62–1; omit it to use the organization's threshold. Lower confidence or blocking issues leave proposals in Review.
* Request instructions constrain generated copy. Review the generated description for content requirements such as excluding warranty and return information.
* This endpoint does not publish changes to marketplaces. It does not need `await_commerce_jobs` or an AOP.
* Do not blindly resubmit after a network timeout: the batch may already exist. Inspect Review before retrying. Concurrent enrichment of the same product can return `409`.

## Windmill queue-only script

Bind `omni` to your authorized connection configuration, with `apiKey` supplied from secret storage. Pass the complete `productIds` array directly to the script, including all 500 IDs if needed.

```typescript theme={null}
export async function main(
  omni: { baseUrl: string; apiKey: string },
  productIds: string[],
) {
  const response = await fetch(
    new URL("/api/v1/products/enrich", omni.baseUrl),
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${omni.apiKey}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        productIds,
        selectedFields: ["description"],
        instructions:
          "Do not change the title. Do not add warranty or return information.",
        autoApplyHighConfidence: true,
        enrichmentAutoApplyThreshold: 0.7,
      }),
    },
  );
  const receipt = await response.json();
  if (!response.ok || !receipt.ok) {
    throw new Error(
      receipt.error ?? `Enrichment request failed (${response.status})`,
    );
  }
  return {
    status: receipt.status,
    batchJobId: receipt.data.batchJobId,
    queuedCount: receipt.data.succeededCount,
    reviewUrl: new URL(receipt.links.review, omni.baseUrl).href,
  };
}
```

The script ends after queue acceptance and returns a compact receipt. Background workers complete enrichment. See [Run Windmill scripts](/guides/scripts) for deploying and invoking a script directly.
