v1Developers guide

Build with shamz.ai

Complete API reference, SDKs, and integration guides for developers. One key, one REST API — widget, catalogue, carts and webhooks.

Base URLhttps://api.shamz.ai
AI agents
1<script src="https://widget.shamz.ai/v1/shamz-widget.js"></script>
2<script>
3 ShamzWidget.init({
4 chatbotId: 'YOUR_CHATBOT_ID',
5 bubblePosition: 'bottom-right'
6 });
7</script>
Reading with an AI coding agent? Give it the Markdown.
Download .md
Getting started

Live in four steps.

01

Get your API keys

Sign up and retrieve your AI front desk ID and API credentials from the dashboard.

02

Embed the widget

Add the embed script and initialisation code to your website.

03

Configure webhooks

Set up endpoints to receive real-time events from chat conversations.

04

Test and deploy

Verify your integration in sandbox mode, then switch to production.

Authentication

There are two kinds of key. Widget and chat requests send your API key in the X-API-KEY header.

Partner endpoints — the Catalogue Push API — take a merchant key as a bearer token in Authorization.

Note

Never expose API keys in client-side code.

Terminalbash
1curl https://api.shamz.ai/api/widget/chat \
2 -H "X-API-KEY: your_api_key" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "msg": {
6 "type": "text",
7 "content": "Hello"
8 }
9 }'

For AI coding agents

Building with Claude Code, Cursor or Copilot? Point the agent at plain Markdown instead of this page — no layout, no scripts, every endpoint and payload intact.

Or switch this page to Markdown view and copy it in one click.

shamz.ai/llms.txtmarkdown
1# shamz.ai
2> AI front desk for Gulf businesses — REST API,
3> embeddable widget, Catalogue Push API, webhooks.
4
5## For developers integrating with us
6- [Catalogue Push API reference](https://shamz.ai/api/catalogue-push.md)
7- [Developers guide](https://shamz.ai/developers-guide.md)
8- [Postman collection](https://shamz.ai/postman/shamz-public-api.postman_collection.json)
9
10## API
11- Base URL: https://api.shamz.ai
12- Widget auth: X-API-KEY header
13- Partner auth: Authorization: Bearer shamz_live_…
Widget

Widget embedding

Basic integration

Add the shamz.ai widget to your website by including our embed script. Replace YOUR_API_KEY with your actual API key from the dashboard.

index.htmlhtml
1<script src="https://widget.shamz.ai/v1/shamz-widget.js"></script>
2<script>
3 ShamzWidget.init({
4 chatbotId: 'YOUR_CHATBOT_ID',
5 bubblePosition: 'bottom-right'
6 });
7</script>

Configuration options

ParameterTypeDefaultDescriptionchatbotIdstringrequiredThe chatbot to connect to. Found in the dashboard.apiBaseUrlstring'https://api.shamz.ai'Only needed if you are pointed at another environment.bubblePositionstring'bottom-right''bottom-right' | 'bottom-left'bubbleSizestring'medium''small' | 'medium' | 'large'primaryColorstringfrom dashboardOverrides the accent colour set on the chatbot.languagestringfrom dashboard'en' | 'ar' — overrides the server-side setting.autoOpenbooleanfalseOpen the widget automatically after autoOpenDelay seconds.
REST API

Talk to the front desk

POST/api/widget/chat

Send a message

Send a message to the AI front desk. Include session_id to keep conversation context, and an optional survey with the customer’s details.

Request bodyjson
1{
2 "session_id": "sess_123",
3 "msg": {
4 "type": "text",
5 "content": "I want to book an appointment"
6 },
7 "survey": {
8 "name": "John Doe",
9 "email": "john@example.com",
10 "phone": "+97312345678"
11 }
12}
Catalogue Push API

Send us your catalogue.

For shops whose own software sends us the catalogue rather than letting us read it. If your system cannot expose an API to us — or you would rather it did not — push to ours instead. The assistant then answers from what you send: product names, prices, stock and specifications.

Postman collection

Five calls ready to run, with a worked example body and tests that check the contract — not only the status code. Set apiKey in the Variables tab and send request 1.

Getting a key

The merchant creates it in their shamz dashboard under E-commerce → Push API, or in the shamz app under Shop → API keys.

The secret is shown once and stored scrambled, so it cannot be recovered — if it is lost, create another and switch the old one off. Switched-off keys stop working on the very next request.

Terminalbash
1curl https://api.shamz.ai/api/partners/v1/status \
2 -H "Authorization: Bearer shamz_live_a1b2c3d4e5f6a7b8_YOUR_SECRET"
PUT/api/partners/v1/catalog

Send your catalogue

Your whole catalogue. Anything you have sent before and leave out of this call is taken off sale — never send a partial list here. Answers 202: products are saved before we reply, images are fetched just after.

external_idRequired
Your own id — how we recognise the same product next time.
name · price · currencyRequired
What the assistant quotes back to customers. An unknown currency code is rejected rather than guessed.
images · urlRecommended
WhatsApp product cards need both. Without either a product is still saved and searchable, but cannot appear in a carousel — the response tells you which.
specsOptional
Goes to the assistant’s knowledge, not the card — so it can answer “does it support eSIM”. Leave out anything you do not know rather than sending “not specified”.
sequenceOptional
Any number that goes up — unix seconds or milliseconds both work. Stops a late retry putting an old price back. Only skips updates; a full catalogue is always applied.

You can also send shop-wide answers alongside the products — delivery, returns, warranty — and the assistant uses them the same way it uses specs. Matching questions are replaced, not duplicated.

PUT /catalogjson
1{
2 "items": [
3 {
4 "external_id": "SKU-1021",
5 "name": "iPhone 16 Pro 256GB",
6 "name_ar": "آيفون 16 برو 256 جيجا",
7 "price": 389.5,
8 "currency": "KWD",
9 "stock_qty": 4,
10 "category": "Phones",
11 "url": "https://your-shop.com/p/iphone-16-pro",
12 "images": ["https://your-shop.com/img/iphone16pro.jpg"],
13 "specs": { "Storage": "256GB", "RAM": "8GB", "Warranty": "1 year" },
14 "sequence": 1792008852
15 }
16 ]
17}
Shop knowledgejson
1{
2 "shop": {
3 "knowledge": [
4 { "question": "Do you deliver across Kuwait?",
5 "answer": "Yes, within 24 hours. Orders over KWD 50 ship free." },
6 { "question": "What is the warranty?",
7 "answer": "One year from the local agent on all phones." }
8 ]
9 },
10 "items": [ ... ]
11}
PATCH/api/partners/v1/items

Update prices and stock

Send as often as you like. Values are absolute, not adjustments, and nothing is ever taken off sale here. This is the one to call when stock moves.

PATCH /itemsjson
1{
2 "items": [
3 { "external_id": "SKU-1021", "stock_qty": 2, "sequence": 1792008852 },
4 { "external_id": "SKU-1044", "price": 42.0, "in_stock": false, "sequence": 1792008852 }
5 ]
6}
POST/api/partners/v1/carts

Carts

Optional, and separate from the catalogue. Send us carts and the assistant can follow up on the ones left behind — a WhatsApp message with the basket and a link back to it, on a schedule the merchant sets.

Send the current contents whenever the cart changes — what you send replaces what we held, so a retry is harmless. Upsert only: a cart you leave out is untouched.

recovery_url is required because the reminder is a link back to the cart. A customer_phone or customer_email is required because without one there is nobody to remind. A cart missing either is rejected, not stored.

discount_code is optional and is your code, applied by your checkout. We never invent one for a pushed shop.

POST /cartsjson
1{
2 "carts": [{
3 "external_id": "CART-9911",
4 "recovery_url": "https://your-shop.com/cart/9911",
5 "customer_phone": "+96555512345",
6 "total_amount": 429.5,
7 "currency": "KWD",
8 "items": [
9 { "external_id": "SKU-1021", "name": "iPhone 16 Pro 256GB",
10 "quantity": 1, "price": 429.5 }
11 ],
12 "discount_code": "COMEBACK10"
13 }]
14}
POST/api/partners/v1/orders

Orders

An order was placed. This stops a shopper who has already bought from being chased, and makes follow-ups measurable — the cart records which reminder was last sent before the order arrived. Send every order; the response tells you which matched a cart.

Note

Carts and orders allow 300 requests a minute. The merchant must switch cart follow-ups on first — until then these calls answer normally but store nothing, and say so.

POST /ordersjson
1{
2 "orders": [{
3 "external_id": "SO-55021",
4 "cart_external_id": "CART-9911",
5 "customer_phone": "+96555512345",
6 "total_amount": 429.5,
7 "currency": "KWD",
8 "discount_code": "COMEBACK10"
9 }]
10}
GET/api/partners/v1/status

Check what we hold

How many products are live, how many are ready for a WhatsApp carousel, and when you last sent us anything. Check your own integration without asking us.

carousel_ready differs by place, deliberately: in the push response it counts products you just sent with a photo and a link; here it counts what we can show right now — lower until photos arrive.

Image results appear here a few seconds after a push. image_errors is capped at 25 entries, keyed by your external_id, and images_at says which push they belong to.

GET /status → 200json
1{
2 "items_active": 412,
3 "items_inactive": 3,
4 "carousel_ready": 402,
5 "last_full_push_at": "2026-01-14T15:17:02Z",
6 "last_update_at": "2026-01-14T15:41:20Z",
7 "last_run": {
8 "kind": "snapshot",
9 "at": "2026-01-14T15:17:02Z",
10 "accepted": 412, "created": 18, "updated": 394, "deactivated": 3,
11 "rejected": [{ "external_id": "SKU-9", "error": "unknown currency 'XY'" }],
12 "warnings": { "SKU-77": ["no_image"] },
13 "images_stored": 394, "images_skipped": 18,
14 "image_errors": { "SKU-77": "HTTP 404" },
15 "images_at": "2026-01-14T15:17:09Z"
16 }
17}

A complete example

PHP, because that is what most shop systems in the region run. Python too. Both are run against production before they ship, so they work as printed.

1<?php
2$key = 'shamz_live_a1b2c3d4e5f6a7b8_YOUR_SECRET';
3$body = json_encode(['items' => [[
4 'external_id' => 'SKU-1021',
5 'name' => 'iPhone 16 Pro 256GB',
6 'price' => 389.5,
7 'currency' => 'KWD',
8 'stock_qty' => 4,
9 'url' => 'https://your-shop.com/p/iphone-16-pro',
10 'images' => ['https://your-shop.com/img/iphone16pro.jpg'],
11 'sequence' => time(),
12]]], JSON_UNESCAPED_UNICODE);
13
14$ch = curl_init('https://api.shamz.ai/api/partners/v1/catalog');
15curl_setopt_array($ch, [
16 CURLOPT_CUSTOMREQUEST => 'PUT',
17 CURLOPT_POSTFIELDS => $body,
18 CURLOPT_RETURNTRANSFER => true,
19 CURLOPT_HTTPHEADER => [
20 'Authorization: Bearer ' . $key,
21 'Content-Type: application/json',
22 ],
23]);
24echo curl_exec($ch);

Responses and limits

One bad row never costs you the rest of the catalogue. You get back what was accepted, what was rejected and why, and any warnings.

401The key is unknown, wrong or switched off. Never says which.
409A push is already running, or the shop is not set up yet.
413Body too large. 8 MB and 2,000 items for a catalogue; 1 MB and 1,000 for an update.
429One full catalogue every 5 minutes, 60 updates a minute. Honour Retry-After.

Image links must be https and publicly reachable; we host our own copy, because WhatsApp will not deliver a card whose image it cannot load. Send links, not base64.

How current your data is, is yours to own. We answer customers from the last thing you sent and never call back to your system — pick a cadence that matches how fast your stock moves.

Response → 202json
1{
2 "accepted": 412, "created": 18, "updated": 394, "deactivated": 3,
3 "carousel_ready": 402, "images_pending": 18,
4 "rejected": [{ "external_id": "SKU-9", "error": "unknown currency 'XY'" }],
5 "warnings": { "SKU-77": ["no_image"] }
6}
Webhooks

Hear about it the moment it happens.

Events

Subscribe to receive real-time notifications about events in your AI front desk.

conversation.startedNew conversation initiated
conversation.completedConversation ended
appointment.createdNew appointment booked
appointment.cancelledAppointment cancelled
ticket.createdNew service desk ticket
ticket.updatedTicket status changed
lead.capturedNew lead captured
lead.qualifiedLead scored as hot/warm
appointment.createdjson
1{
2 "event": "appointment.created",
3 "timestamp": "2026-01-14T10:30:00Z",
4 "data": {
5 "appointment_id": "apt_123",
6 "customer_name": "John Doe",
7 "customer_phone": "+97312345678",
8 "appointment_type": "Consultation",
9 "scheduled_at": "2026-01-14T14:00:00Z",
10 "agent_name": "Dr. Smith"
11 }
12}

Setting up webhooks

1Go to your AI front desk settings in the dashboard
2Navigate to Integrations → Webhooks
3Enter your webhook URL and select events to subscribe to
4Save and test your webhook
SDKs & libraries

Official SDKs

None are published yet. The REST API above is the integration surface today — it is fully documented, and the Postman collections run against it.

JavaScript / TypeScript

Coming soon

A typed client for the REST API. Until it ships, call the endpoints directly — every one of them is documented above.

Python

Coming soon

For backend integrations and automation. The Catalogue Push examples show the same calls with `requests`.

React component

Coming soon

A drop-in component. The embed script already works in React — see the example below.

Example: React

No package needed — the embed script is a plain script tag, so a small component that loads it once and calls init is the whole integration. This is what the React SDK will wrap when it ships.

ShamzChat.jsxjsx
1import { useEffect } from 'react';
2
3const SRC = 'https://widget.shamz.ai/v1/shamz-widget.js';
4
5export default function ShamzChat({ chatbotId }) {
6 useEffect(() => {
7 // The bundle attaches window.ShamzWidget, so load it once and
8 // initialise after it resolves.
9 let script = document.querySelector(`script[src="${SRC}"]`);
10 if (!script) {
11 script = document.createElement('script');
12 script.src = SRC;
13 script.async = true;
14 document.body.appendChild(script);
15 }
16
17 const start = () => window.ShamzWidget?.init({ chatbotId });
18 if (window.ShamzWidget) start();
19 else script.addEventListener('load', start);
20
21 return () => window.ShamzWidget?.destroy?.();
22 }, [chatbotId]);
23
24 return null;
25}
Operations

Rate limits & best practices

API rate limits

Starter
60requests / minute
10concurrent connections
Growth
300requests / minute
50concurrent connections
Enterprise
Customrequests / minute
Customconcurrent connections

Best practices

Use session IDsAlways include session_id to maintain conversation context.
Handle errorsImplement proper error handling and retry logic.
Cache responsesCache static content like FAQs and service lists.
Webhooks over pollingUse webhooks instead of polling for real-time updates.
Secure your keysNever expose API keys in client-side code.
Monitor usageTrack your API usage to avoid hitting rate limits.

Error handling

On 429, back off and retry. Throw on any other non-2xx, and always show the customer a fallback message.

chat.jsjavascript
1try {
2 const response = await fetch('https://api.shamz.ai/api/widget/chat', {
3 method: 'POST',
4 headers: {
5 'X-API-KEY': apiKey,
6 'Content-Type': 'application/json'
7 },
8 body: JSON.stringify(payload)
9 });
10
11 if (!response.ok) {
12 if (response.status === 429) {
13 // Rate limit hit, implement exponential backoff
14 await sleep(1000);
15 return retry();
16 }
17 throw new Error(`API error: ${response.status}`);
18 }
19
20 const data = await response.json();
21 return data;
22} catch (error) {
23 console.error('Shamz API error:', error);
24 // Fallback to error message
25}

Need technical support?

Have questions about API implementation or need help with integration? Our technical team is here to assist.