HomeDevelopers

Developer Docs

Feed, transactional APIs & widget embed

Connect ChatAxon to any e-commerce platform — product catalog sync, add-to-cart, discount codes, order tracking, and the chat widget embed.

Overview

Start here

ChatAxon supports full feature parity across all e-commerce platforms — not just WooCommerce. The integration is split into two layers:

Product Catalog

Feed URL or webhook — point ChatAxon at your product catalog. Supports Google Merchant XML, Shopify JSON, or a custom endpoint.

Transactional

Add to cart, apply discounts, and track orders — via a JSON endpoint you expose and configure in the widget.

FeatureWooCommerceOther platforms
Product search & AI chat✅ Auto✅ Feed sync
Add to cart✅ Native✅ JSON endpoint
Apply discount code✅ Native⚠️ Copy code (or JSON endpoint)
Order tracking (WIMO)✅ Native✅ Server endpoint
Real-time stock updates✅ Webhook✅ Webhook
WooCommerce users don't need this guide — install the WordPress plugin for a fully automatic native integration.

Feed URL Setup

Dashboard

In the ChatAxon dashboard, open Store Settings → Product Feed Integration and paste your feed URL. Select a format or leave it on Auto-detect.

1

Paste your feed URL

Accepts any publicly accessible URL. The feed must return XML or JSON. No authentication on the feed URL is supported yet.

2

Click "Test Feed"

We fetch the first 5 products and show them in a preview table. Confirm the format is correct.

3

Click "Sync Now"

A background job fetches all products, generates AI embeddings, and indexes them. Large catalogs may take a few minutes.

4

Embed the widget

Copy your chataxon_ API key and follow the instructions below.

Google Merchant Center XML

Recommended

The most widely supported format. If you already run Google Shopping ads, you already have this feed. Both RSS 2.0 (<rss><channel><item>) and Atom (<feed><entry>) are supported.

Platforms with built-in export

  • PrestaShop — built-in Google Shopping module
  • Magento / Adobe Commerce — native data feed
  • Wix eCommerce — Marketing → Google Shopping
  • BigCommerce — Channel Manager → Google Shopping
  • WooCommerce — plugins: Product Feed PRO, WOOSEA

Minimal example

xml
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">
  <channel>
    <title>My Store</title>
    <item>
      <g:id>SKU-001</g:id>
      <g:title>Nike Air Max 90</g:title>
      <g:description>Classic everyday sneaker.</g:description>
      <g:price>129.99 USD</g:price>
      <g:image_link>https://mystore.com/img/nike.jpg</g:image_link>
      <g:link>https://mystore.com/products/nike-air-max-90</g:link>
      <g:product_type>Sneakers</g:product_type>
      <g:availability>in stock</g:availability>
    </item>
  </channel>
</rss>

Recognized fields

FieldRequiredDescription
g:idrequiredUnique product identifier
g:titlerequiredProduct name shown in chat
g:descriptionoptionalLong text used for AI search
g:priceoptionalNumeric price with currency code (e.g. 29.99 USD)
g:image_linkoptionalPrimary product image URL
g:linkoptionalProduct page URL
g:product_typeoptionalCategory label
g:availabilityoptional"in stock" or "out of stock"

ChatAxon JSON

For custom platforms

The simplest option if your developer is building a custom endpoint. Serve a JSON response at any URL — we handle the rest.

The endpoint must be publicly accessible (no auth). Serve it over HTTPS. Recommended Content-Type: application/json.

Format

json
{
  "products": [
    {
      "id": "123",
      "name": "Nike Air Max 90",
      "description": "Classic everyday sneaker, available in 5 colors.",
      "price": 129.99,
      "image": "https://mystore.com/img/nike.jpg",
      "url": "https://mystore.com/products/nike-air-max-90",
      "category": "Sneakers",
      "in_stock": true,
      "variant_id": "123-42"
    }
  ]
}
FieldRequiredDescription
idrequiredUnique product identifier (string or number)
namerequiredProduct name
descriptionoptionalProduct description (HTML stripped automatically)
priceoptionalNumeric price (no currency symbol)
imageoptionalPrimary image URL
urloptionalProduct page URL
categoryoptionalCategory string
in_stockoptionalBoolean or "in stock" / "out of stock"
variant_idoptionalVariant identifier passed to addToCart hook (size, color, etc.)
javascript
// GET /chataxon-feed.json
app.get('/chataxon-feed.json', async (req, res) => {
  const products = await db.query(
    'SELECT id, name, description, price, image_url, slug FROM products WHERE active = 1'
  );
  res.json({
    products: products.map(p => ({
      id:          String(p.id),
      name:        p.name,
      description: p.description,
      price:       parseFloat(p.price),
      image:       p.image_url,
      url:         `https://mystore.com/products/${p.slug}`,
      in_stock:    true,
    }))
  });
});

Shopify — Product Feed

Zero config

Every Shopify store exposes a public /products.json endpoint. No plugin or app install required.

1

Enter your store URL

Paste https://your-store.myshopify.com — or your custom domain — into the Feed URL field. Select Auto-detect or Shopify.

2

That's it

ChatAxon automatically calls /products.json?limit=250&page=N, paginates through all pages (up to 5 000 products), and indexes everything including variant IDs.

Shopify's /products.json is public by default. If you have a password-protected store (development mode), the endpoint will return 401 and the sync will fail.

For add-to-cart, discounts, and order tracking on Shopify, see

Cart Integration

JSON endpoint

The ChatAxon widget can add products directly to your cart on any platform by calling a JSON endpoint you host. If cart.addToCartUrl isn't configured, the widget falls back to WooCommerce's own ajax cart (?wc-ajax=add_to_cart), so WordPress stores need no configuration here at all.

Set window.ChatAxonConfig.cart before the ChatAxon widget script executes.

Config shape

typescript
window.ChatAxonConfig.cart = {
  // Required to activate the cart adapter at all.
  addToCartUrl: 'https://yourstore.com/api/cart/add',

  // Extra headers merged into every request — this is where you pass your
  // platform's own session id / CSRF token. Read them fresh at page-load
  // time (e.g. from a cookie) since the config is built once per page load.
  headers: { 'X-Session-ID': '...' },
};

What the widget sends

http
POST https://yourstore.com/api/cart/add
Content-Type: application/json
<...your configured headers>

{ "product_id": "123", "quantity": 1 }

A 2xxresponse is treated as success (the widget shows “Added!” and does not need any particular response body). Any other status is treated as a failure.

Implementation example

javascript
// Read whatever your framework already uses for session/CSRF at page-load
// time, then wire it into the widget config before the script tag loads.
window.ChatAxonConfig = {
  apiKey: 'chataxon_YOUR_API_KEY',
  apiUrl: 'https://api.chataxon.com',
  cart: {
    addToCartUrl: '/api/cart/add',
    headers: { 'X-Session-ID': getMySessionId() },
  },
};
If your backend uses session-cookie CSRF protection (e.g. Laravel Sanctum), include the CSRF token as a header too — the widget makes a plain fetch()call, it does not read cookies or inject CSRF tokens for you the way a framework's HTTP client might.

Discount Codes

WooCommerce
WooCommerce only. The [COUPON:CODE] mechanism requires WooCommerce API keys — the AI fetches active coupons live from your WooCommerce admin (/wp-json/wc/v3/coupons) and only offers codes it finds there. There is currently no way to configure a coupon list for non-WooCommerce stores, so the AI will not proactively offer discounts on custom/feed-based integrations.

When the AI offers a discount, it embeds a tag in its response: [COUPON:CODE] (e.g. [COUPON:SUMMER20]). The widget intercepts this tag and renders a code card instead of showing it as plain text. What happens when the shopper clicks it depends on whether you've configured cart.applyCouponUrl (see ):

  • Not configured (default for non-WooCommerce): clicking the code copies it to the clipboard. This covers most custom storefronts, which usually have their own promo field at checkout rather than a live-apply endpoint.
  • WooCommerce: applied directly to the live WooCommerce cart via the plugin's own ajax handler — no configuration needed.
  • cart.applyCouponUrl configured: the widget POSTs { code } as JSON to your endpoint instead of copying.

applyCouponUrl config

javascript
window.ChatAxonConfig.cart = {
  addToCartUrl:   '/api/cart/add',       // required for the cart adapter to activate
  applyCouponUrl: '/api/cart/coupon',    // optional — omit to use copy-to-clipboard instead
  headers: { 'X-Session-ID': getMySessionId() },
};

Your endpoint receives POST { code: "SUMMER20" } and should return a 2xx for a successfully applied code, any other status otherwise. The widget does not require a specific response body — it does not parse discount amounts back out today.

Order Tracking API

WooCommerce-shaped

When a customer asks “Where is my order?”, the AI collects an order ID and email in chat, then calls the same store endpoint used for real-time product sync — there is no separate order-tracking URL to configure. It requests GET {store_url}/wp-json/wc/v3/orders/{orderId}, authenticated with HTTP Basic Auth using the same consumer key/secret pair from your Connect Store step.

This means order tracking currently requires the store_url + consumer key/secret setup (the same one that powers real-time single-product sync) — a catalog synced purely via feed_url has no order-tracking endpoint to call yet.
1

Expose one endpoint per order

GET {store_url}/wp-json/wc/v3/orders/{orderId} — the exact path WooCommerce itself uses. orderId is whatever the customer typed (their order number), not necessarily numeric.

2

Authenticate the same way as product sync

HTTP Basic Auth: Authorization: Basic base64(consumer_key:consumer_secret).

3

Return billing.email — it's the security check

ChatAxon compares the email the customer typed in chat against billing.emailin your response (case-insensitive). A mismatch is treated as “not your order” and nothing is disclosed.

Request

http
GET https://your-store.com/wp-json/wc/v3/orders/ORD-12345
Authorization: Basic base64(consumer_key:consumer_secret)

Response — order found

json
{
  "id": 12345,
  "status": "shipped",
  "billing": { "email": "customer@email.com" },

  // Everything below is optional. A plain WooCommerce order never has
  // these fields — the AI only mentions tracking info when present.
  "tracking_number": "1Z999AA10123456784",
  "carrier": "ups",
  "status_description": "In Transit",
  "shipped_at": "2026-06-08T14:30:00Z",
  "estimated_delivery": "2026-06-15T18:00:00Z"
}

Response — order not found

Return an HTTP 404 — there is no found: false body to construct.

FieldRequiredDescription
idrequiredOrder identifier (echoed back, not otherwise used)
statusrequiredAny string — combined with your store's configured status-message mapping in the ChatAxon dashboard
billing.emailrequiredUsed for the security check against what the customer typed in chat
tracking_numberoptionalCarrier tracking number
carrieroptionale.g. "ups", "fedex" — shown uppercased next to the tracking number
status_descriptionoptionalHuman-readable carrier status ("In Transit", "Delivered"…)
shipped_atoptionalISO 8601 — when the AI mentions a ship date
estimated_deliveryoptionalISO 8601 — when the AI mentions an ETA

Implementation examples

javascript
// GET /wp-json/wc/v3/orders/:orderId
app.get('/wp-json/wc/v3/orders/:orderId', requireBasicAuth, async (req, res) => {
  const order = await db.orders.findOne({ where: { number: req.params.orderId } });
  if (!order) return res.status(404).json({});

  res.json({
    id: order.id,
    status: order.status,
    billing: { email: order.customerEmail },
    // Optional — only include what you actually have:
    tracking_number: order.trackingNumber ?? undefined,
    carrier: order.carrier ?? undefined,
    status_description: order.carrierStatusText ?? undefined,
    shipped_at: order.shippedAt?.toISOString(),
    estimated_delivery: order.estimatedDelivery?.toISOString(),
  });
});

Shopify: Cart & Orders

Proxy required

Shopify's Ajax Cart API (/cart/add.js) expects { id: variantId, quantity }, but the ChatAxon widget always POSTs { product_id, quantity } to whatever URL you configure — the field name doesn't match, so you need a small proxy function rather than pointing addToCartUrl straight at Shopify.

Add to cart — proxy function

Deploy this as a small serverless function (Vercel/Netlify/Cloudflare Worker) on your own domain, then set cart.addToCartUrl to it. It forwards to Shopify's storefront using the customer's own session — the widget request must run through the shopper's browser (so it needs credentials: 'include', which the widget already sends).

javascript
// /api/shopify-cart-add.js — deployed on your own domain, proxied to Shopify
export default async function handler(req, res) {
  const { product_id, quantity } = req.body; // product_id = Shopify variant ID
                                              // (set this as the feed's product id for Shopify stores)
  const shopifyRes = await fetch(`https://${process.env.SHOPIFY_SHOP_DOMAIN}/cart/add.js`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'Cookie': req.headers.cookie ?? '' },
    body: JSON.stringify({ id: Number(product_id), quantity }),
  });
  res.status(shopifyRes.ok ? 200 : 422).json({});
}
javascript
window.ChatAxonConfig.cart = {
  addToCartUrl: 'https://yourdomain.com/api/shopify-cart-add',
};

Discount codes

Shopify has no same-domain endpoint to apply a code without a redirect. The simplest reliable option: leave cart.applyCouponUrl unconfigured so the widget copies the code to the clipboard, and tell shoppers to paste it at checkout — or redirect via /discount/{code}, which sets the code as a cookie Shopify auto-applies at checkout.

Order tracking on Shopify

Same contract as — a GET {store_url}/wp-json/wc/v3/orders/{orderId} endpoint with Basic Auth. For Shopify, that endpoint is a proxy function that queries the Shopify Admin API server-side:

javascript
// GET /wp-json/wc/v3/orders/:orderId  (route this path on your own domain/proxy)
export default async function handler(req, res) {
  // 1. Basic Auth check (same consumer key/secret as Connect Store)
  const auth = Buffer.from((req.headers.authorization || '').split(' ')[1] || '', 'base64').toString();
  if (auth !== `${process.env.CHATAXON_CONSUMER_KEY}:${process.env.CHATAXON_CONSUMER_SECRET}`) {
    return res.status(401).end();
  }

  // 2. Query Shopify Admin API — order name e.g. "#1234"
  const sfRes = await fetch(
    `https://${process.env.SHOPIFY_SHOP_DOMAIN}/admin/api/2024-01/orders.json?name=%23${req.query.orderId}`,
    { headers: { 'X-Shopify-Access-Token': process.env.SHOPIFY_ADMIN_TOKEN } }
  );
  const { orders } = await sfRes.json();
  const order = orders?.[0];
  if (!order) return res.status(404).end();

  const fulfillment = order.fulfillments?.[0];

  res.json({
    id: order.order_number,
    status: order.fulfillment_status ?? order.financial_status ?? 'pending',
    billing: { email: order.email },
    tracking_number: fulfillment?.tracking_number,
    carrier: fulfillment?.tracking_company,
  });
}
SHOPIFY_ADMIN_TOKEN is a private Admin API access token — it stays server-side in this function, never in the widget config.

Real-time Webhook

Instant sync

When products change on your platform, fire a POST to our webhook endpoint. ChatAxon immediately re-fetches your feed and re-indexes it — your AI assistant stays up to date within seconds.

Endpoint

http
POST https://api.chataxon.com/api/store/feed/webhook

Headers:
  x-api-key: chataxon_YOUR_API_KEY
  Content-Type: application/json
  X-ChatAxon-Signature: <your_webhook_secret>   # optional but strongly recommended

Body (all fields optional):
{
  "event": "product.updated"  // product.created | product.deleted | catalog.updated
}
About the webhook signature: The X-ChatAxon-Signature header is optional — if omitted, the request is still accepted when the API key is valid. If included, it must be the pre-computed HMAC-SHA256 hex value tied to your store. Retrieve it once from GET https://api.chataxon.com/api/store/feed/webhook-secret (authenticated with your API key) and store it as an environment variable on your server. Do not compute it per-request — just send the static value you retrieved.

Get your webhook secret (one-time setup)

bash
# Run once. Store the returned webhook_secret in your server's environment variables.
curl -s https://api.chataxon.com/api/store/feed/webhook-secret \
  -H "x-api-key: chataxon_YOUR_API_KEY"

# Response:
# { "webhook_secret": "a3f1c8...64 hex chars..." }
202

Accepted

Sync job queued successfully

401

Unauthorized

Invalid API key or signature

422

Unprocessable

No feed URL configured for this store

Rate limit

Max 20 calls per hour per API key. For catalogs that change frequently, batching is fine — the queue deduplicates rapid calls. If you exceed the limit, you receive a 429 Too Many Requests response.

Code examples

Replace chataxon_YOUR_API_KEY with your actual API key and CHATAXON_WEBHOOK_SECRET with the value returned by the /webhook-secret endpoint above.

bash
# Set these in your environment first:
# CHATAXON_API_KEY=chataxon_YOUR_API_KEY
# CHATAXON_WEBHOOK_SECRET=<value from /api/store/feed/webhook-secret>

curl -X POST https://api.chataxon.com/api/store/feed/webhook \
  -H "x-api-key: $CHATAXON_API_KEY" \
  -H "X-ChatAxon-Signature: $CHATAXON_WEBHOOK_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"event": "catalog.updated"}'

CSAT Rating

Optional

Show a satisfaction prompt at the end of a conversation (👍 / 👎) and send the result to ChatAxon. The rating appears in your analytics dashboard under CSAT Score.

Fire the event from your widget UI (after the user clicks 👍 or 👎) using the POST /api/store/track endpoint:

Endpoint

http
POST https://api.chataxon.com/api/store/track
x-api-key: chataxon_YOUR_API_KEY
x-session-id: <current chat session UUID>
Content-Type: application/json

{
  "event_type": "csat",
  "metadata": {
    "rating": "positive"   // "positive" | "negative"
  }
}

JavaScript widget example

javascript
// Call this after showing the thumbs-up / thumbs-down prompt
async function sendCSAT(rating) {
  await fetch('https://api.chataxon.com/api/store/track', {
    method: 'POST',
    headers: {
      'Content-Type':  'application/json',
      'x-api-key':     window.ChatAxonConfig.apiKey,
      'x-session-id':  window.ChatAxon.getSessionId(), // exposed by the widget
    },
    body: JSON.stringify({
      event_type: 'csat',
      metadata:   { rating }, // 'positive' | 'negative'
    }),
  });
}

// Wire up your buttons
document.getElementById('thumbs-up').addEventListener('click',   () => sendCSAT('positive'));
document.getElementById('thumbs-down').addEventListener('click', () => sendCSAT('negative'));
FieldRequiredDescription
event_typerequiredMust be exactly "csat"
metadata.ratingrequired"positive" or "negative"
Only one CSAT event per session is counted. Sending a second rating for the same x-session-id will be recorded but may skew averages — gate the prompt so it appears only once.

Widget Embed

Non-WordPress

Add the ChatAxon chat widget to any website — no CMS required. Include the script tag before </body>.

<div id="chataxon-root"> must already be in the DOM before widget.js runs — it looks the element up once, synchronously, at load time (it does not wait for DOMContentLoaded). Placing the div and scripts together right before </body>, in this order, satisfies that.

Minimal embed

html
<!-- ChatAxon Widget -->
<div id="chataxon-root"></div>
<script>
  window.ChatAxonConfig = {
    apiKey: 'chataxon_YOUR_API_KEY',
    apiUrl: 'https://api.chataxon.com',
  };
</script>
<link rel="stylesheet" href="https://api.chataxon.com/widget.css">
<script src="https://api.chataxon.com/widget.js" defer></script>

Full configuration

html
<script>
  window.ChatAxonConfig = {
    // Required
    apiKey:  'chataxon_YOUR_API_KEY',
    apiUrl:  'https://api.chataxon.com',

    // Appearance
    themeColor:     '#1E364B',      // hex color for buttons and accents
    widgetPosition: 'bottom-right', // 'bottom-right' | 'bottom-left'
    offsetBottom:   24,             // pixels from bottom edge
    offsetSide:     24,             // pixels from side edge

    // Mobile overrides (null = inherit from desktop)
    mobileWidgetPosition: null,
    mobileOffsetBottom:   null,
    mobileOffsetSide:     null,

    // Behaviour
    defaultOpen:    false,          // open chat on page load
    bubbleText:     'Need help?',   // speech bubble above widget
    welcomeMessage: 'Hi! How can I help you today?',

    // Internationalisation
    currency: '€',                  // symbol passed to price display

    // Cart adapter (optional) — see Cart Integration. Omit entirely on
    // WordPress; the widget falls back to WooCommerce's own ajax cart.
    cart: {
      addToCartUrl:   '/api/cart/add',
      applyCouponUrl: '/api/cart/coupon', // optional
      headers: { 'X-Session-ID': '...' },
    },
  };
</script>
FieldRequiredDescription
apiKeyrequiredYour chataxon_ store key (from the dashboard)
apiUrlrequiredAlways https://api.chataxon.com — also where /widget.js and /widget.css are served from
themeColoroptionalHex color for the widget accent (#1E364B default)
widgetPositionoptional"bottom-right" (default) or "bottom-left"
offsetBottomoptionalPixels from bottom edge (default 24)
offsetSideoptionalPixels from side edge (default 24)
mobileWidgetPositionoptionalOverride position on mobile. null = use desktop setting
defaultOpenoptionalOpen chat automatically on page load (default false)
bubbleTextoptionalShort text in the speech bubble shown above the button
welcomeMessageoptionalFirst message shown in the chat window
currencyoptionalCurrency symbol used by the widget for price display only (default $). Not stored server-side — prices in the feed are used as-is.
cart.addToCartUrloptionalSee Cart Integration — enables non-WooCommerce add-to-cart
cart.applyCouponUrloptionalSee Discount Codes — omit to copy codes to clipboard instead
The apiKey is safe to include in client-side code. It is scoped to your store and only permits reading product data and sending chat messages — no write access.