You'll get an email with a reset password link.
Back to Sign In
Connect any e-commerce platform to AI Sales Agent. Implement REST endpoints, secure with authentication, and sync products & orders for an AI-powered shopping assistant.
The Generic API connector lets you integrate any REST-capable e-commerce platform with AI Sales Agent. Follow these four steps to get started.
Add /health, /api/products, and /api/orders endpoints to your store backend. Paths are fully customizable.
Create a dedicated API key, bearer token, or basic-auth user specifically for AI Sales Agent. Scope it read-only.
In the Commerce Dashboard, add a Generic API store. Enter your base URL, auth credentials, and endpoint paths.
Trigger an initial product and order sync. Optionally configure webhooks for real-time updates. Your chatbot is ready.
End-to-end data path from setup through live chatbot usage.
Your API must expose the following three endpoints. All endpoints must:
/health
Default path (customizable)
Health check endpoint. AI Sales Agent calls this during connection and reconnection to verify your API is reachable. Must return HTTP 200.
{
"status": "ok",
"timestamp": "2024-01-15T10:30:00Z"
}
/api/products
Default path (customizable)
Returns a paginated list of products. AI Sales Agent iterates all pages until exhausted.
Query parameters sent by AI Sales Agent: ?page=1&per_page=50
{
"data": [
{
"id": "prod_123",
"name": "Wireless Headphones",
"description": "Premium noise-cancelling wireless headphones with 40-hour battery life.",
"short_description": "40hr battery, noise-cancelling, Bluetooth 5.2",
"sku": "WH-1000XM5",
"price": 349.99,
"regular_price": 399.99,
"sale_price": 349.99,
"stock_status": "in_stock",
"stock_quantity": 42,
"categories": ["Electronics", "Audio"],
"tags": ["wireless", "bluetooth", "noise-cancelling"],
"images": [
"https://yourstore.com/images/wh-1000xm5-main.jpg",
"https://yourstore.com/images/wh-1000xm5-side.jpg"
],
"attributes": {
"color": "Black",
"connectivity": "Bluetooth 5.2",
"battery": "40 hours"
}
}
],
"meta": {
"current_page": 1,
"per_page": 50,
"total": 250,
"last_page": 5
}
}
id,
name.
AI Sales Agent will skip any product missing both fields. All other fields are optional but recommended for richer chatbot answers.
/api/orders
Default path (customizable)
Returns a paginated list of orders. Used for order-status queries and customer service in the chatbot.
{
"data": [
{
"id": "order_456",
"order_number": "ORD-2024-0456",
"status": "completed",
"total": 699.98,
"subtotal": 649.98,
"tax": 32.50,
"shipping": 17.50,
"currency": "USD",
"customer_email": "alice@example.com",
"customer_name": "Alice Johnson",
"items": [
{
"product_id": "prod_123",
"name": "Wireless Headphones",
"quantity": 2,
"price": 349.99
}
],
"shipping_address": {
"street": "123 Main St",
"city": "New York",
"state": "NY",
"zip": "10001",
"country": "US"
},
"billing_address": {
"street": "123 Main St",
"city": "New York",
"state": "NY",
"zip": "10001",
"country": "US"
},
"payment_method": "credit_card",
"created_at": "2024-01-15T10:30:00Z"
}
],
"meta": {
"current_page": 1,
"per_page": 50,
"total": 1200,
"last_page": 24
}
}
id.
All other fields are optional. Provide as much detail as possible to enable order-status queries from the chatbot.
AI Sales Agent supports three authentication methods. Choose the one your platform supports and configure it in your API middleware. AI Sales Agent sends the credential on every request.
AI Sales Agent sends the API key as a custom HTTP header on every request. This is the simplest and most widely supported method.
GET /api/products HTTP/1.1
Host: api.yourstore.com
X-API-Key: sk_live_4f8d2a1b9c3e7f6a0b2d4e8c1a3f5b7d
Accept: application/json
Read the X-API-Key header value and compare it against a stored secret. Reject with HTTP 401 if it does not match.
AI Sales Agent sends the token in the standard Authorization header. Supports both static tokens and JWTs.
GET /api/products HTTP/1.1
Host: api.yourstore.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Accept: application/json
A long, randomly generated string stored securely. Validate by direct comparison.
A signed JSON Web Token. Validate the signature and expiry claim using your secret key.
AI Sales Agent encodes username:password in Base64 and sends it in the Authorization header.
GET /api/products HTTP/1.1
Host: api.yourstore.com
Authorization: Basic Ym90bWVyemU6c3VwZXJzZWNyZXQ=
Accept: application/json
// Decoded value: base64("ai sales agent:supersecret")
You create a credential on your own store (an API key, token, or username/password) and paste it into AI Sales Agent when connecting. AI Sales Agent takes it from there — storing it securely and attaching it to every request it makes to your API.
Generate a credential in your store
Use your platform's built-in API key manager, admin panel, or a simple config value. The credential only needs to work for read-only access to your products and orders endpoints.
Copy and paste it into AI Sales Agent
When adding your store in the Commerce Dashboard, select the auth method that matches what your backend expects, then paste the value into the corresponding field.
AI Sales Agent handles the rest
AI Sales Agent securely stores the credential and automatically attaches it to every sync request it sends to your API. You never need to manage it again unless you rotate it.
Creates a secure random token in your browser. Works for all three auth methods — API Key, Bearer Token, or Basic Auth password. Copy it, add it to your store config, then paste the same value into AI Sales Agent when connecting.
AI Sales Agent sends the token as
X-API-Key: <token>.
Your store reads that header and compares the value against what you configured.
X-API-Key: sk_live_4f8d2a1b9c3e7f6a...
You can use the generated token above as this value.
AI Sales Agent sends the same token as Authorization: Bearer <token>.
The token value is identical — only the header format differs from API Key.
Authorization: Bearer sk_live_4f8d2a1b9c3e7f6a...
You can use the generated token above as this value too — just validate the Authorization header instead of X-API-Key.
AI Sales Agent encodes username:password
in Base64 and sends it as Authorization: Basic <encoded>.
You can use the generated token as the password.
ai sales agentsk_live_4f8d2a1b9c3e7f6a... ← use generated tokenAuthorization: Basic Ym90bWVyemU6c2tfbGl2ZV8...
Once you have your credentials, you need to configure three things inside your e-commerce store:
| Variable | What it is | Where to get it |
|---|---|---|
| AI SALES AGENT_API_KEY | The token AI Sales Agent sends on every request to your store. Your middleware validates it. | Generate it on this page ↑ and paste the same value into AI Sales Agent when connecting. |
| AI SALES AGENT_WEBHOOK_URL | The base URL of your AI Sales Agent installation. Your store POSTs events here. | The root URL you use to access AI Sales Agent, e.g. https://app.ai sales agent.com |
| AI SALES AGENT_STORE_TOKEN | Identifies your store in webhook URLs. Included in every webhook path. | Commerce Dashboard → select your store → copy the Store Token shown on the detail page. |
AI SALES AGENT_API_KEY=sk_live_87db8f090a2544ac2bd658ebd6f042db...
AI SALES AGENT_WEBHOOK_URL=https://app.ai sales agent.com
AI SALES AGENT_STORE_TOKEN=88bedac0-a8da-4700-8d07-5dbbf4fdfd6d
<?php
// config/ai sales agent.php
return [
'api_key' => env('AI SALES AGENT_API_KEY', ''),
'webhook_url' => env('AI SALES AGENT_WEBHOOK_URL', ''),
'store_token' => env('AI SALES AGENT_STORE_TOKEN', ''),
];
<?php
// app/Http/Middleware/AI Sales AgentAuth.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class AI Sales AgentAuth
{
public function handle(Request $request, Closure $next)
{
$expected = config('ai sales agent.api_key');
$incoming = $request->header('X-API-Key');
if (empty($expected) || $incoming !== $expected) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $next($request);
}
}
// routes/api.php
Route::middleware(\App\Http\Middleware\AI Sales AgentAuth::class)->group(function () {
Route::get('/health', fn() => response()->json(['status' => 'ok']));
Route::get('/api/products', [ProductController::class, 'index']);
Route::get('/api/orders', [OrderController::class, 'index']);
});
<?php
// app/Services/AI Sales AgentWebhookService.php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class AI Sales AgentWebhookService
{
public static function sendProductEvent(string $event, $product): void
{
$base = config('ai sales agent.webhook_url');
$token = config('ai sales agent.store_token');
if (empty($base) || empty($token)) {
return; // not configured yet — skip silently
}
$url = rtrim($base, '/') . '/api/generic-api/webhooks/' . $token . '/products';
Http::post($url, [
'event' => $event, // product.created | product.updated | product.deleted
'data' => [
'id' => (string) $product->id,
'name' => $product->name,
'price' => (float) ($product->sale_price ?? $product->price ?? 0),
'stock_status' => $product->stock > 0 ? 'in_stock' : 'out_of_stock',
],
]);
}
public static function sendOrderEvent(string $event, $order): void
{
$base = config('ai sales agent.webhook_url');
$token = config('ai sales agent.store_token');
if (empty($base) || empty($token)) {
return;
}
$url = rtrim($base, '/') . '/api/generic-api/webhooks/' . $token . '/orders';
Http::post($url, [
'event' => $event, // order.created | order.updated | order.deleted
'data' => [
'id' => (string) $order->id,
'order_number' => $order->order_number,
'status' => $order->status,
'total' => (float) $order->total,
'customer_email' => $order->customer_email,
],
]);
}
}
<?php
// app/Observers/ProductObserver.php
use App\Services\AI Sales AgentWebhookService;
class ProductObserver
{
public function created(Product $product): void
{
AI Sales AgentWebhookService::sendProductEvent('product.created', $product);
}
public function updated(Product $product): void
{
AI Sales AgentWebhookService::sendProductEvent('product.updated', $product);
}
public function deleted(Product $product): void
{
AI Sales AgentWebhookService::sendProductEvent('product.deleted', $product);
}
}
// app/Providers/AppServiceProvider.php
Product::observe(ProductObserver::class);
Order::observe(OrderObserver::class);
AI SALES AGENT_API_KEY=sk_live_87db8f090a2544ac2bd658ebd6f042db...
AI SALES AGENT_WEBHOOK_URL=https://app.ai sales agent.com
AI SALES AGENT_STORE_TOKEN=88bedac0-a8da-4700-8d07-5dbbf4fdfd6d
// middleware/ai sales agentAuth.js
function ai sales agentAuth(req, res, next) {
const incoming = req.headers['x-api-key'];
const expected = process.env.AI SALES AGENT_API_KEY;
if (!expected || incoming !== expected) {
return res.status(401).json({ error: 'Unauthorized' });
}
next();
}
// Apply to your store endpoints
app.use(['/health', '/api/products', '/api/orders'], ai sales agentAuth);
// services/ai sales agentWebhook.js
const axios = require('axios');
const base = process.env.AI SALES AGENT_WEBHOOK_URL;
const token = process.env.AI SALES AGENT_STORE_TOKEN;
async function sendProductEvent(event, product) {
if (!base || !token) return;
const url = `${base.replace(/\/$/, '')}/api/generic-api/webhooks/${token}/products`;
await axios.post(url, {
event,
data: {
id: String(product.id),
name: product.name,
price: parseFloat(product.salePrice ?? product.price ?? 0),
stock_status: product.stock > 0 ? 'in_stock' : 'out_of_stock',
},
}).catch(err => console.error('AI Sales Agent webhook failed:', err.message));
}
async function sendOrderEvent(event, order) {
if (!base || !token) return;
const url = `${base.replace(/\/$/, '')}/api/generic-api/webhooks/${token}/orders`;
await axios.post(url, {
event,
data: {
id: String(order.id),
order_number: order.orderNumber,
status: order.status,
total: parseFloat(order.total),
customer_email: order.customerEmail,
},
}).catch(err => console.error('AI Sales Agent webhook failed:', err.message));
}
module.exports = { sendProductEvent, sendOrderEvent };
// Usage in your route/controller after saving:
// await sendProductEvent('product.updated', product);
// await sendOrderEvent('order.created', order);
AI SALES AGENT_API_KEY=sk_live_87db8f090a2544ac2bd658ebd6f042db...
AI SALES AGENT_WEBHOOK_URL=https://app.ai sales agent.com
AI SALES AGENT_STORE_TOKEN=88bedac0-a8da-4700-8d07-5dbbf4fdfd6d
# FastAPI
import os
from fastapi import Header, HTTPException, Depends
def ai sales agent_auth(x_api_key: str = Header(...)):
expected = os.getenv("AI SALES AGENT_API_KEY", "")
if not expected or x_api_key != expected:
raise HTTPException(status_code=401, detail="Unauthorized")
# Apply to your endpoints
@app.get("/health", dependencies=[Depends(ai sales agent_auth)])
def health():
return {"status": "ok"}
@app.get("/api/products", dependencies=[Depends(ai sales agent_auth)])
def list_products(): ...
# services/ai sales agent_webhook.py
import os
import httpx
BASE_URL = os.getenv("AI SALES AGENT_WEBHOOK_URL", "")
STORE_TOKEN = os.getenv("AI SALES AGENT_STORE_TOKEN", "")
def send_product_event(event: str, product) -> None:
if not BASE_URL or not STORE_TOKEN:
return
url = f"{BASE_URL.rstrip('/')}/api/generic-api/webhooks/{STORE_TOKEN}/products"
with httpx.Client() as client:
client.post(url, json={
"event": event, # product.created | product.updated | product.deleted
"data": {
"id": str(product.id),
"name": product.name,
"price": float(product.sale_price or product.price or 0),
"stock_status": "in_stock" if product.stock > 0 else "out_of_stock",
},
})
def send_order_event(event: str, order) -> None:
if not BASE_URL or not STORE_TOKEN:
return
url = f"{BASE_URL.rstrip('/')}/api/generic-api/webhooks/{STORE_TOKEN}/orders"
with httpx.Client() as client:
client.post(url, json={
"event": event, # order.created | order.updated | order.deleted
"data": {
"id": str(order.id),
"order_number": order.order_number,
"status": order.status,
"total": float(order.total),
"customer_email": order.customer_email,
},
})
# Usage — call after saving/updating:
# send_product_event("product.updated", product)
# send_order_event("order.created", order)
Where to find AI SALES AGENT_STORE_TOKEN
Connect your store first (steps in the "Connect to AI Sales Agent" section below), then go to Commerce Dashboard → click your store name → the Store Token is shown on the detail page. Copy it into your .env file.
To make chatbot "View Product" links open the exact product page, your API should return either a full permalink (absolute URL) OR a slug / handle. If a slug is provided, AI Sales Agent constructs the URL as {store_base_url}/product/{slug}. If neither is present, the chatbot will fall back to the store homepage and a warning will be logged.
{
"id": "42",
"name": "Blue Hoodie",
"permalink": "https://store.com/product/blue-hoodie"
}
{
"id": "42",
"name": "Blue Hoodie",
"slug": "blue-hoodie"
}
{
"id": "42",
"name": "Blue Hoodie"
}
| Field | Type | Required | Accepted Aliases | Notes |
|---|---|---|---|---|
| id | string / int | Required | product_id, sku | Unique external ID |
| name | string | Required | title, product_name | Product display name |
| price | float | Optional | selling_price, current_price | Current selling price |
| regular_price | float | Optional | original_price, list_price | Pre-discount price |
| sale_price | float | Optional | discount_price | Discounted price |
| sku | string | Optional | product_sku, code | Stock keeping unit |
| description | string | Optional | full_description, body | HTML or plain text |
| short_description | string | Optional | summary, excerpt | Brief summary |
| stock_status | string | Optional | availability | in_stock, out_of_stock, available… |
| stock_quantity | int | Optional | stock, inventory, quantity | Units available |
| categories | array | Optional | category, product_categories | Array of strings or CSV |
| tags | array | Optional | product_tags, labels | Array of strings or CSV |
| images | array | Optional | photos, gallery, image_urls | Array of absolute image URLs |
| attributes | object / array | Optional | variations, options | Key-value pairs for size/colour etc. |
| permalink | string (URL) | Recommended | product_url, url, link, product_link, html_url, web_url, href, page_url, detail_url | Absolute URL to the product detail page. Required for accurate "View Product" links unless a slug is supplied. |
| slug | string | Recommended | handle, product_slug, permalink_slug, url_key, url_slug, seo_slug |
URL-safe identifier. Used to build
{store_base_url}/product/{slug}
when no permalink is provided.
|
| Field | Type | Required | Accepted Aliases |
|---|---|---|---|
| id | string / int | Required | order_id, order_number |
| order_number | string | Optional | order_no, number |
| status | string | Optional | order_status, state |
| total | float | Optional | total_amount, grand_total, order_total |
| customer_email | string | Optional | email, billing_email |
| customer_name | string | Optional | customer, buyer_name |
| items | array | Optional | line_items, products, order_items |
| currency | string | Optional | currency_code |
| created_at | ISO 8601 / timestamp | Optional | date, order_date, created_date |
AI Sales Agent fetches data page-by-page to handle large catalogs without timeouts. Default page size is 50 items. The request stops when a page returns fewer items than requested.
page — current page number, starting from 1per_page — items per page (default: 50, max: 100){
"data": [ { "id": "1", "name": "..." }, ... ],
"meta": {
"current_page": 1,
"per_page": 50,
"total": 250,
"last_page": 5
}
}
[ { "id": "1", "name": "..." }, { "id": "2", "name": "..." }, ... ]
If your API uses different field names, configure custom field mappings when connecting your store. AI Sales Agent will use your mapping instead of the built-in aliases.
Your API returns products with the field "product_title" instead of "name", and "retail_price" instead of "price". Configure field_mappings to tell AI Sales Agent where to find each value.
{
"name": "product_title",
"price": "retail_price",
"description": "long_description",
"sku": "item_code",
"stock_status": "availability_status",
"stock_quantity":"qty_on_hand",
"categories": "dept_names",
"images": "photo_urls",
"permalink": "product_page_url",
"slug": "url_key"
}
in_stock, instock, available,
true, 1
out_of_stock, outofstock, unavailable,
false, 0
349.99 — number"349.99" — numeric string"$349.99" — currency prefix stripped"USD 349.99" — currency prefix strippedFull working implementations for popular backend frameworks. Each example includes the health endpoint, products, orders, and API key authentication middleware.
use App\Http\Middleware\ValidateBotMergeApiKey;
use Illuminate\Http\Request;
Route::middleware([ValidateBotMergeApiKey::class])->group(function () {
Route::get('/health', fn() => response()->json([
'status' => 'ok',
'timestamp' => now()->toIso8601String(),
]));
Route::get('/api/products', function (Request $request) {
$perPage = min((int) $request->get('per_page', 50), 100);
$products = \App\Models\Product::query()
->where('status', 'active')
->paginate($perPage, ['*'], 'page', $request->get('page', 1));
return response()->json([
'data' => $products->map(fn ($p) => [
'id' => (string) $p->id,
'name' => $p->name,
'description' => $p->description,
'short_description' => $p->short_description,
'sku' => $p->sku,
'price' => (float) $p->price,
'regular_price' => (float) $p->compare_price,
'sale_price' => $p->sale_price ? (float) $p->sale_price : null,
'stock_status' => $p->stock > 0 ? 'in_stock' : 'out_of_stock',
'stock_quantity' => (int) $p->stock,
'categories' => $p->categories->pluck('name')->toArray(),
'tags' => $p->tags->pluck('name')->toArray(),
'images' => $p->images->pluck('url')->toArray(),
'attributes' => $p->attributes ?? [],
]),
'meta' => [
'current_page' => $products->currentPage(),
'per_page' => $products->perPage(),
'total' => $products->total(),
'last_page' => $products->lastPage(),
],
]);
});
Route::get('/api/orders', function (Request $request) {
$perPage = min((int) $request->get('per_page', 50), 100);
$orders = \App\Models\Order::query()
->with(['items', 'shippingAddress', 'billingAddress'])
->paginate($perPage, ['*'], 'page', $request->get('page', 1));
return response()->json([
'data' => $orders->map(fn ($o) => [
'id' => (string) $o->id,
'order_number' => $o->order_number,
'status' => $o->status,
'total' => (float) $o->total,
'currency' => $o->currency ?? 'USD',
'customer_email' => $o->customer_email,
'customer_name' => $o->customer_name,
'items' => $o->items->map(fn ($i) => [
'product_id' => (string) $i->product_id,
'name' => $i->name,
'quantity' => (int) $i->quantity,
'price' => (float) $i->price,
])->toArray(),
'shipping_address' => $o->shippingAddress?->toArray(),
'billing_address' => $o->billingAddress?->toArray(),
'created_at' => $o->created_at->toIso8601String(),
]),
'meta' => [
'current_page' => $orders->currentPage(),
'per_page' => $orders->perPage(),
'total' => $orders->total(),
'last_page' => $orders->lastPage(),
],
]);
});
});
const express = require('express');
const app = express();
// Middleware
function apiKeyAuth(req, res, next) {
const key = req.headers['x-api-key'];
if (!key || key !== process.env.AI SALES AGENT_API_KEY) {
return res.status(401).json({ error: 'Invalid or missing API key' });
}
next();
}
app.use('/health', apiKeyAuth);
app.use('/api', apiKeyAuth);
// Health
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// Products ─
app.get('/api/products', async (req, res) => {
const page = parseInt(req.query.page || '1');
const perPage = Math.min(parseInt(req.query.per_page || '50'), 100);
const offset = (page - 1) * perPage;
const [products, total] = await Promise.all([
Product.findAll({ limit: perPage, offset, where: { status: 'active' } }),
Product.count({ where: { status: 'active' } }),
]);
res.json({
data: products.map(p => ({
id: String(p.id),
name: p.name,
description: p.description,
sku: p.sku,
price: parseFloat(p.price),
stock_status: p.stock > 0 ? 'in_stock' : 'out_of_stock',
stock_quantity: parseInt(p.stock),
categories: p.categories || [],
images: p.images || [],
})),
meta: {
current_page: page,
per_page: perPage,
total,
last_page: Math.ceil(total / perPage),
},
});
});
// Orders
app.get('/api/orders', async (req, res) => {
const page = parseInt(req.query.page || '1');
const perPage = Math.min(parseInt(req.query.per_page || '50'), 100);
const offset = (page - 1) * perPage;
const [orders, total] = await Promise.all([
Order.findAll({ limit: perPage, offset, include: ['items'] }),
Order.count(),
]);
res.json({
data: orders.map(o => ({
id: String(o.id),
order_number: o.orderNumber,
status: o.status,
total: parseFloat(o.total),
currency: o.currency || 'USD',
customer_email: o.customerEmail,
customer_name: o.customerName,
items: o.items.map(i => ({
product_id: String(i.productId),
name: i.name,
quantity: i.quantity,
price: parseFloat(i.price),
})),
created_at: o.createdAt.toISOString(),
})),
meta: {
current_page: page,
per_page: perPage,
total,
last_page: Math.ceil(total / perPage),
},
});
});
app.listen(3000);
from fastapi import FastAPI, Header, HTTPException, Query
from typing import Optional
import os, math
app = FastAPI()
AI SALES AGENT_API_KEY = os.getenv("AI SALES AGENT_API_KEY", "")
def verify_key(x_api_key: str = Header(...)):
if x_api_key != AI SALES AGENT_API_KEY:
raise HTTPException(status_code=401, detail="Invalid API key")
@app.get("/health")
def health(key=Depends(verify_key)):
return {"status": "ok", "timestamp": datetime.utcnow().isoformat() + "Z"}
@app.get("/api/products")
def list_products(
page: int = Query(1, ge=1),
per_page: int = Query(50, ge=1, le=100),
key=Depends(verify_key),
):
total = db.query(Product).filter_by(status="active").count()
products = (db.query(Product)
.filter_by(status="active")
.offset((page - 1) * per_page)
.limit(per_page)
.all())
return {
"data": [
{
"id": str(p.id),
"name": p.name,
"description": p.description,
"sku": p.sku,
"price": float(p.price),
"stock_status": "in_stock" if p.stock > 0 else "out_of_stock",
"stock_quantity": int(p.stock),
"categories": p.categories or [],
"images": p.images or [],
}
for p in products
],
"meta": {
"current_page": page,
"per_page": per_page,
"total": total,
"last_page": math.ceil(total / per_page),
},
}
@app.get("/api/orders")
def list_orders(
page: int = Query(1, ge=1),
per_page: int = Query(50, ge=1, le=100),
key=Depends(verify_key),
):
total = db.query(Order).count()
orders = (db.query(Order).offset((page - 1) * per_page).limit(per_page).all())
return {
"data": [
{
"id": str(o.id),
"order_number": o.order_number,
"status": o.status,
"total": float(o.total),
"customer_email": o.customer_email,
"customer_name": o.customer_name,
"items": [{"product_id": str(i.product_id), "quantity": i.quantity, "price": float(i.price)} for i in o.items],
"created_at": o.created_at.isoformat() + "Z",
}
for o in orders
],
"meta": {
"current_page": page,
"per_page": per_page,
"total": total,
"last_page": math.ceil(total / per_page),
},
}
Instead of waiting for the next scheduled sync, configure your store to push changes to AI Sales Agent instantly via webhooks. When a product or order is created, updated, or deleted in your store, fire a POST request to the AI Sales Agent webhook endpoint.
POST
https://aibot.theb2b.cn/api/generic-api/webhooks/{store_token}/products
POST
https://aibot.theb2b.cn/api/generic-api/webhooks/{store_token}/orders
POST
https://aibot.theb2b.cn/api/generic-api/webhooks/{store_token}/sync
POST /api/generic-api/webhooks/{store_token}/products
Content-Type: application/json
{
"event": "product.updated",
"data": {
"id": "prod_123",
"name": "Wireless Headphones",
"price": 349.99,
"stock_status": "in_stock",
"description": "Updated description after firmware change.",
"images": ["https://yourstore.com/images/wh-v2.jpg"]
}
}
{
"event": "product.deleted",
"data": {
"id": "prod_123"
}
}
POST /api/generic-api/webhooks/{store_token}/orders
Content-Type: application/json
{
"event": "order.created",
"data": {
"id": "order_789",
"order_number": "ORD-2024-0789",
"status": "pending",
"total": 349.99,
"customer_email": "bob@example.com",
"customer_name": "Bob Smith",
"items": [
{ "product_id": "prod_123", "quantity": 1, "price": 349.99 }
]
}
}
AI Sales Agent responds with HTTP 200 on success. Your store should not retry if it receives 200. Retry on 500.
HTTP/1.1 200 OK
Content-Type: application/json
{
"success": true,
"message": "Webhook processed"
}
use Illuminate\Support\Facades\Http;
class ProductObserver
{
private string $webhookUrl = 'https://ai sales agent.com/api/generic-api/webhooks/YOUR_STORE_TOKEN/products';
public function updated(Product $product): void
{
Http::post($this->webhookUrl, [
'event' => 'product.updated',
'data' => [
'id' => (string) $product->id,
'name' => $product->name,
'price' => (float) $product->price,
'stock_status' => $product->stock > 0 ? 'in_stock' : 'out_of_stock',
],
]);
}
public function deleted(Product $product): void
{
Http::post($this->webhookUrl, [
'event' => 'product.deleted',
'data' => ['id' => (string) $product->id],
]);
}
}
Once your API endpoints are live and credentials are ready, follow these steps in the AI Sales Agent dashboard.
Navigate to Commerce Dashboard
Go to your AI Sales Agent dashboard → Commerce → click "Add Store".
Select Generic API
Choose "Generic API" from the store type list.
Enter Base URL
Provide the root URL of your API, e.g.
https://api.yourstore.com.
Do not include a trailing slash or endpoint path.
Select Auth Method & Enter Credentials
Choose API Key, Bearer Token, or Basic Auth. Paste the credential you generated on your backend.
Configure Endpoints (optional)
If your paths differ from the defaults, override them:
Configure Field Mappings (optional)
If your field names differ from the defaults, provide a JSON mapping as described in the Field Mapping section.
Click Connect
AI Sales Agent tests connectivity by calling /health. On success, the store is activated and the initial product and order sync is dispatched automatically.
Monitor Sync Progress
Watch the Sync Runs section on the store detail page. Once products are synced and embeddings are generated, your AI chatbot is ready.
Follow these guidelines to keep your store API secure when opening it to AI Sales Agent.
Always use HTTPS
Never expose API endpoints over plain HTTP. TLS ensures credentials and data cannot be intercepted in transit. AI Sales Agent rejects non-HTTPS URLs for production stores.
Scope credentials to read-only
Create a dedicated API key or user for AI Sales Agent with read-only access to products and orders only. AI Sales Agent never writes data to your store.
Never store raw API keys in plain text
Hash keys with bcrypt before storing in your database. Show the raw key only once at creation time.
Rate-limit your endpoints
Apply rate limiting (e.g. 100 requests per minute per API key) to prevent accidental overload. AI Sales Agent respects 429 Too Many Requests and will retry with backoff.
Whitelist AI Sales Agent IP addresses (optional)
For extra security, restrict the API endpoints to only accept traffic from AI Sales Agent servers at the firewall or Nginx level.
Rotate credentials regularly
Generate a new API key every 90 days. Disconnect and reconnect your store in AI Sales Agent with the new key. Immediately revoke the old key.
Log and monitor API access
Log every request to your API endpoints including IP, timestamp, and key fingerprint. Alert on unusual access patterns.
Validate webhook payloads (if adding a signature)
When sending webhooks from your store, optionally include an HMAC-SHA256 signature in the X-Webhook-Signature header. Verify it on both sides to prevent spoofed webhook calls.
Common integration issues and how to resolve them.
Check:
curl -I -H "X-API-Key: YOUR_KEY" https://api.yourstore.com/health
curl -H "X-API-Key: YOUR_KEY"
"https://api.yourstore.com/api/products?page=1&per_page=1"
curl "…/api/products?page=1&per_page=50" # should return 50 items
curl "…/api/products?page=2&per_page=50" # should return next 50
curl "…/api/products?page=999&per_page=50" # should return 0 or fewer items
curl -X POST \
-H "Content-Type: application/json" \
-d '{"event":"product.updated","data":{"id":"prod_1","name":"Test"}}' \
https://ai sales agent.com/api/generic-api/webhooks/YOUR_STORE_TOKEN/products
Still stuck? Open the sync run in your dashboard to read the detailed error message. For further support, include the sync run ID and a curl reproduction case when contacting support.