Platform Integrations
Connect your storefront to Razlio. We support native connections for Shopify and WooCommerce, as well as a fully agnostic Custom REST API interface for bespoke setups like Django, Laravel, or Express.
Custom REST API
Razlio communicates with your store strictly over two HTTP endpoints that you host and maintain. You configure a custom auth header in our dashboard, which we pass on every request.
/productsWe poll this on a schedule and pull product changes since the last sync.
/ordersWhen our AI agent successfully places an order, we send the structured payload to you here.
GET /api/products HTTP/1.1 Host: your-store.com Authorization: Bearer sk_live_123456
Return 401 Unauthorized if this header is missing or incorrect.
When you configure the header name as Authorization in the dashboard and paste a raw token (e.g. sk_live_…), we automatically prefix it with Bearer on every outgoing request. If you already typed Bearer …, Basic …, Token … etc., we leave it alone. Custom header names like X-API-Key are sent verbatim.
Try it — build your cURL
Fill in the same values you will enter in the dashboard and copy the commands below. These are byte-identical to the requests Razlio sends, so if they work in your terminal the integration will work.
curl -sS -i -X GET 'https://your-store.com/api/razlio/products?limit=1' \ -H 'Authorization: Bearer YOUR_TOKEN' \ -H 'Accept: application/json' \ -H 'Content-Type: application/json'
curl -sS -X GET 'https://your-store.com/api/razlio/products?updated_at_min=2026-05-03T00:00:00' \ -H 'Authorization: Bearer YOUR_TOKEN' \ -H 'Accept: application/json' \ -H 'Content-Type: application/json'
curl -sS -i -X POST 'https://your-store.com/api/razlio/orders' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"order_number": "RZL-00042",
"currency": "BDT",
"customer": {
"name": "Rahim Ahmed",
"phone": "01712345678",
"email": null
},
"delivery": {
"address": "House 12, Road 5, Dhanmondi, Dhaka",
"zone": "Dhaka City",
"cost": "60.00"
},
"payment_method": "cod",
"items": [
{
"external_id": "SKU-RED-SHIRT-M",
"variant_external_id": "SKU-RED-SHIRT-L",
"product_name": "Red T-Shirt",
"variant_name": "Size: L",
"quantity": 2,
"unit_price": "600.00",
"line_total": "1200.00"
}
],
"subtotal": "1200.00",
"total": "1260.00",
"notes": null,
"created_at": "2026-05-03T09:14:00+00:00"
}'{base}{products_path}Products Endpoint
Returns a JSON array of your products. On subsequent syncs, we pass a timestamp via query param to perform a highly efficient delta sync.
updated_at_minISO 8601Return products updated at or after this timestamp. We always send it timezone-aware, in UTC: 2026-05-03T00:00:00+00:00. Parse it as ISO 8601 rather than a fixed format — a naive parser rejects the offset.
On the first sync, and on any full re-sync, the parameter is omitted entirely. Return your whole catalogue when it is absent.
[
{
"external_id": "SKU-RED-SHIRT-M",
"name": "Red T-Shirt",
"description": "100% cotton crew-neck.",
"category": "Clothing",
"price": "550.00",
"compare_at_price": "650.00",
"currency": "BDT",
"sku": "SKU-RED-SHIRT-M",
"slug": "red-t-shirt",
"source_url": "https://yourshop.com/products/red-t-shirt",
"status": "active",
"tags": ["new", "summer"],
"images": [
{ "url": "https://cdn.example.com/red-m.jpg", "alt": "Red shirt", "is_primary": true }
],
"variants": [
{
"name": "Size",
"value": "M",
"price_adjustment": "0.00",
"sku": "SKU-RED-SHIRT-M",
"stock_quantity": 12
},
{
"name": "Size",
"value": "L",
"price_adjustment": "50.00",
"sku": "SKU-RED-SHIRT-L",
"stock_quantity": 4
}
]
},
{
"external_id": "MUG-CERAMIC-01",
"name": "Ceramic Mug",
"description": "350ml, dishwasher safe.",
"category": "Kitchen",
"price": "250.00",
"currency": "BDT",
"sku": "MUG-CERAMIC-01",
"slug": "ceramic-mug",
"source_url": "https://yourshop.com/products/ceramic-mug",
"status": "active",
"stock_quantity": 40,
"tags": ["gift"],
"images": [
{ "url": "https://cdn.example.com/mug.jpg", "alt": "Ceramic mug", "is_primary": true }
],
"variants": []
}
]Product Field Reference
Only external_id is required. Every other field falls back to the value in the third column, so a missing field is silently accepted — but a field of the wrong shape rejects that product. Rejected products are skipped, counted, and reported back to the merchant with a reason; the rest of the catalogue still imports.
Product
| Field | Type | If omitted | Notes |
|---|---|---|---|
external_idrequired | string | — | Your stable internal ID. This is the matching key for every future sync, so it must not change for the lifetime of the product — re-keying creates a duplicate rather than an update. Max 128 characters. |
name | string | "Untitled" | Max 255 characters. Part of what the AI searches over. |
description | string | null | null | Searched by the AI. Plain text or HTML both work. |
category | string | null | null | A single category name, max 120 characters. Not a path and not a list. |
price | string | number | "0.00" | Decimal, up to 12 digits with 2 decimal places. Send "550.00", not "৳550" or "1,550" — a currency symbol or thousands separator rejects the row. |
compare_at_price | string | number | null | null | The struck-through "was" price. Same format as price. |
currency | string | "BDT" | Max 8 characters. |
sku | string | null | null | Max 120 characters. Shown to the merchant; not used for matching. |
slug | string | null | null | What your storefront calls this product in its own URL — the adidas-running-comfort-blue-red in yourshop.com/products/adidas-running-comfort-blue-red. Combined with your store's storefront URL it becomes the link the AI gives customers. Send it exactly as your storefront writes it, dots and percent-escapes included: it is used as-is and never rewritten. A slug we cannot use verbatim (a raw space, a slash, a ?) is discarded, and the link is then derived from the product name as though no slug had been sent. A slug in a non-Latin script is still a valid slug — send it percent-encoded, the way WooCommerce stores it, and it is used as-is. When no slug is sent the link is derived from the product name instead, but only when that name is plain unaccented A–Z once accents are folded (Café is fine, Blåbær is not). Anything else gets no link at all, because a partial transliteration is a URL that looks right and 404s. Max 255 characters. |
source_url | string | null | null | The product's own page, in full. Takes priority over slug — it is the only URL we know resolves, so send it if you have it. Also accepted as url or permalink, whichever your shop software calls it. Never send an image or media URL here: those show no preview in Messenger and open a raw image file when tapped. Photos are delivered as real attachments from the images field instead. Must be a complete http(s) URL on a single line — anything else is ignored and the slug is used instead. |
status | "active" | "draft" | "archived" | "active" | Only active products are visible to the AI. archived is the removal signal — see below. Any other value rejects the row. |
stock_quantity | integer | null | null | Product-level stock, used only when the product has no variants. Omit it and the product is always available; send 0 and it is out of stock. Omitting and sending zero are different claims. |
in_stock | boolean | null | null | Availability without a count, for merchants who do not track quantities. Ignored when stock_quantity is present. |
tags | string[] | [] | Must be an array. A comma-separated string is not split — it is iterated character by character. |
images | object[] | [] | Each entry is an object with url, alt and is_primary. An array of bare URL strings rejects the row. |
variants | object[] | [] | One entry per purchasable combination — see the variants rule below. |
Variant
| Field | Type | If omitted | Notes |
|---|---|---|---|
name | string | "Option" | The axis label, e.g. Size. Max 64 characters. |
value | string | "" | The full combination, e.g. M or M / Red. Max 120 characters. |
price_adjustment | string | number | null | null | Added to the product price for this combination. May be negative. |
sku | string | null | null | Max 120 characters. |
stock_quantity | integer | 0 | Zero means this combination is unavailable. When a product has variants, these values decide availability and the product-level stock fields are ignored. |
Keep external_id stable
It is the only thing tying your product to ours. If it is derived from something mutable — a SKU, a slug, a variant — then editing that field makes us insert a second product instead of updating the first.
One variant row per purchasable SKU
Variants are matched on name plus value together. For a two-axis product put the whole combination in value ("M / Red"), one row each. Sending Size and Colour as separate rows describes the axes but loses which combinations actually exist, so per-combination price and stock cannot be represented.
Removing a product
Send it with status: "archived". Dropping it from the feed is not a removal signal — a delta sync cannot tell a deleted product from an unchanged one, so it stays live and the AI keeps offering it.
{base}{orders_path}Orders Endpoint
You must persist this incoming JSON payload in your database and respond with your newly created internal order ID.
order_number twice due to a network timeout, treat it as the same order and return the existing external_id.{
"order_number": "RZL-00042",
"currency": "BDT",
"customer": {
"name": "Rahim Ahmed",
"phone": "01712345678",
"email": null
},
"delivery": {
"address": "House 12, Road 5, Dhanmondi, Dhaka",
"zone": "Dhaka City",
"cost": "60.00"
},
"payment_method": "cod",
"items": [
{
"external_id": "SKU-RED-SHIRT-M",
"variant_external_id": "SKU-RED-SHIRT-L",
"product_name": "Red T-Shirt",
"variant_name": "Size: L",
"quantity": 2,
"unit_price": "600.00",
"line_total": "1200.00"
}
],
"subtotal": "1200.00",
"total": "1260.00",
"notes": null,
"created_at": "2026-05-03T09:14:00+00:00"
}{
"external_id": "ORDER-9821"
}Shopify Integration
Razlio connects to your Shopify store using the official Shopify Admin REST API. We use cursor-based pagination and incremental polling to efficiently map your entire catalog.
Setup Guide
- Log into your Shopify Admin dashboard.
- Navigate to Settings > Apps and sales channels.
- Click Develop apps and then Create an app (Name it "Razlio Sync").
- Under Configuration, click "Configure" next to Admin API Integration.
- Assign the required permissions (see right sidebar) and click Save.
- Go to API Credentials, click Install app, and reveal your Admin API Access Token.
- Paste this Token along with your `.myshopify.com` domain into the Razlio Dashboard.
Required API Scopes
read_productsread_inventorywrite_ordersread_customers
WooCommerce Integration
Razlio connects to your WordPress/WooCommerce site using the core WooCommerce REST API (v3) via HTTP Basic Auth. We utilize the modified_after filter for lightning-fast delta syncs.
Setup Guide
- Log into your WordPress wp-admin dashboard.
- Navigate to WooCommerce > Settings > Advanced > REST API.
- Click Add key.
- Add a Description (e.g., "Razlio Integration").
- Set Permissions to Read/Write.
- Click Generate API key.
- Copy the Consumer Key and Consumer Secret before leaving the page.
- Paste these keys along with your base URL into the Razlio Dashboard.
Required Details
- Base URL
https://your-store.com - Consumer Key
ck_... - Consumer Secret
cs_...
Website Chat Widget
The Razlio chat widget is a single-tag drop-in script that puts your AI agent on any website. It loads asynchronously, makes zero network calls before the visitor opens the bubble, and only ~12 KB gzipped over the wire. Conversations sync to the same inbox as your Messenger and WhatsApp threads.
Where to get your tokens
- Go to Dashboard → Channels → Website Widget.
- Click Enable Widget if it isn't already.
- Copy the
data-tokenshown on the panel.
The token is a public identifier — safe to paste in HTML. It only proves "this page belongs to a store that has the widget enabled". The real auth boundary is a session JWT issued by the server when the visitor opens the bubble.
Embed Snippet
Paste this one line as close to </body> as you can. That's it. The bubble will appear in the bottom-right of every page that includes the script.
YOUR_WIDGET_TOKEN with the value from your dashboard. The token always starts with wgt_ followed by 32 characters.<script async
src="https://app.razlio.com/widget.js"
data-token="YOUR_WIDGET_TOKEN"
data-api="https://app-api.razlio.com"></script>Platform-Specific Setup
Shopify
- Admin → Online Store → Themes.
- Click Actions → Edit code on your live theme.
- Open
layout/theme.liquid. - Paste the snippet just above
</body>. - Save. Refresh your store — bubble appears.
WordPress / WooCommerce
- Install the free WPCode (Insert Headers and Footers) plugin.
- Go to Code Snippets → + Add Snippet → HTML Snippet.
- Paste the script in the Footer location.
- Set Active and Save.
- Or: theme
footer.phpjust before</body>.
Next.js (App Router)
In your root app/layout.tsx:
import Script from "next/script";
export default function RootLayout({ children }) {
return (
<html><body>
{children}
<Script async
src="https://app.razlio.com/widget.js"
data-token={process.env.NEXT_PUBLIC_RAZLIO_TOKEN}
data-api="https://app-api.razlio.com" />
</body></html>
);
}Plain HTML / Static Site
- Open each
.htmlpage (or your shared template). - Paste the snippet just before the closing
</body>tag. - Re-deploy. Works on Netlify, Vercel, GitHub Pages, S3, anywhere.
Google Tag Manager (any site)
Useful when you can't edit your theme directly. GTM injects the snippet at runtime — works on Shopify Plus, Webflow, Wix, Squarespace, etc.
- GTM → Tags → New → Custom HTML.
- Paste the Razlio script into the HTML field.
- Trigger: All Pages (or any page-view trigger).
- Save → Submit the workspace version.
Configuration Attributes
All configuration lives on the <script> tag itself as data-* attributes. No JavaScript API to call.
data-tokenrequiredYour public widget token. Get it from Dashboard → Channels → Website Widget.
data-apioptionalAPI base. Defaults to https://api.razlio.com (production). On staging or self-hosted deployments, set this to https://app-api.razlio.com.
asyncrecommendedStandard HTML attribute. Lets the browser load the widget without blocking page rendering. Always include.
<!-- Razlio Chat Widget --> <script async src="https://app.razlio.com/widget.js" data-token="wgt_a1b2c3d4e5f6...32chars" data-api="https://app-api.razlio.com"> </script>
How the Sync Engine Works
All platform integrations are powered by our core asynchronous task runner. Here's exactly how we guarantee consistency across platforms:
- 1Scheduled Polling & Cursors
Our worker cluster polls your endpoint every 5 hours. On successful completion we write alast_synced_attimestamp to our database. You can also trigger a sync yourself at any time from the dashboard. - 2The 5-Minute Safety Buffer
On the next run, we take that timestamp, subtract exactly 5 minutes (to prevent race conditions with in-flight transactions), and send it back to you as theupdated_at_min(or WooCommerce'smodified_after). - 3Rate Limit Backoffs (429 / 5xx)
On a 429 or a 5xx we read yourRetry-Afterheader, wait, and retry once. Without that header we wait 2 seconds; we never wait longer than 30. A 4xx other than 429 is not retried — we treat it as your answer.
Integration Support
Getting authentication errors or missing products? Reach out to our technical support engineers directly.
Contact Support