ACE (Agentic Commerce Engine)

ACE SDK

The official JavaScript and TypeScript client for the ACE v1 API.

The ACE SDK is the official JavaScript and TypeScript client for ACE (Agentic Commerce Engine). Use it in Node.js apps, scripts, or serverless functions to run the Contextual Enrichment Engine, manage durable jobs, and read your account usage.

Who this is for

Developers calling ACE from their own code. If you would rather drive ACE from a terminal, see the ACE CLI. If you want an AI assistant to call ACE for you, see the MCP server.

Install

npm install @authoritas-ace/ace-sdk

Create a client

import { createClient } from "@authoritas-ace/ace-sdk";

const ace = createClient({
  baseUrl: "https://ace.authoritas.com",
  apiKey: process.env.ACE_API_KEY, // ace_live_... or ace_test_...
});

Create a key under Settings, Developer console, Keys. See API Keys for scopes and environments.

Your first call

health() needs no key, so it is the quickest way to confirm your base URL is right.

const res = await ace.health();
console.log(res.status, res.data); // 200 { status: "ok" }

How results are shaped

Every method resolves to an ApiResult<T>. Nothing throws on an API error: you get error instead of data, plus the HTTP status.

interface ApiResult<T> {
  data?: T;
  error?: { code: string; message: string; details?: unknown };
  status: number;
}

Check error yourself, or use assertOk to turn a failure into a thrown AceApiError:

import { assertOk, AceApiError } from "@authoritas-ace/ace-sdk";

try {
  const res = await ace.usage();
  assertOk(res);
  console.log(res.data.credits.balance); // res.data is defined past this point
} catch (err) {
  if (err instanceof AceApiError) {
    console.error(err.code, err.status, err.message);
  }
}

Contextual Enrichment Engine

Three methods, matching the three engine endpoints. Each takes a source, which is either inline products or a connected store.

// 1. Generate the eight section contextual rules for a product set.
const rules = await ace.enrichment.rules({
  source: {
    type: "inline",
    products: [
      { id: "sku-1", title: "Merino base layer", description: "...", category: "Outdoor" },
    ],
  },
  creativityLevel: 3,
});

// 2. Generate content grounded in those rules.
const content = await ace.enrichment.content({
  source: { type: "inline", products: [{ id: "sku-1", title: "Merino base layer" }] },
  contentTypes: ["product-description", "meta-tags", "jsonld-schema"],
  rules: rules.data,
});

// 3. Or run both steps in one call.
const pipeline = await ace.enrichment.pipeline({
  source: { type: "inline", products: [{ id: "sku-1", title: "Merino base layer" }] },
  contentTypes: ["product-description", "meta-tags"],
});

A product needs only id and title. Extra keys you pass through (brand, price, tags, attributes) become grounding signal.

Idempotency

Write methods accept an idempotency key, so a retried request reuses the original job instead of starting a second one.

await ace.enrichment.pipeline(body, { idempotencyKey: "nightly-2026-07-29" });

Jobs

A large content or pipeline request runs asynchronously and returns a job envelope. Poll it, or let the SDK poll for you.

const started = await ace.enrichment.pipeline({ source, contentTypes: ["product-description"] });
assertOk(started);

// Poll until the job reaches a terminal status (default: every 2s, 5 minute cap).
const finished = await ace.jobs.wait(started.data.id, { pollMs: 2000, timeoutMs: 300_000 });
assertOk(finished);

// Then page through the per-product output.
const results = await ace.jobs.results(started.data.id, { page: 1, pageSize: 50 });

The full set:

MethodWhat it does
ace.jobs.list({ kind, status, storeId, page, pageSize })List jobs, newest first.
ace.jobs.get(id)Fetch one job with its status and result.
ace.jobs.results(id, { page, pageSize })Paginated per-product results.
ace.jobs.cancel(id)Cancel a queued or running job.
ace.jobs.wait(id, { pollMs, timeoutMs })Poll until terminal. Returns a TIMEOUT error if the cap elapses.

Utilities

Fast helpers. Language detection is unbilled and the scorers are pure functions, so none of these consume credits.

await ace.utils.language({ products });          // dominant locale + confidence
await ace.utils.agenticReadiness({ products });  // per-product readiness + summary
await ace.utils.reviewQuality({ items });        // weighted overall score per item

Usage and feeds

const usage = await ace.usage();
// { env, credits: { balance, limit, periodStart, periodEnd },
//   rateLimit: { limitPerMinute }, jobs: { total }, testQuota? }

await ace.feeds.list({ projectId });
await ace.feeds.get(feedId);

testQuota is present only for ace_test_ keys.

Escape hatch

Any endpoint without a typed wrapper is still reachable, with auth and error handling applied:

await ace.get("/api/v1/webhooks");
await ace.post("/api/v1/webhooks", {
  url: "https://example.com/hook",
  events: ["enrichment.job.succeeded"],
});

Next steps

On this page