Getting started
Introduction
The Barrq API lets a merchant's own website talk to Barrq server to server. Report each order and Barrq messages the customer on WhatsApp at every status change, in their language. Optionally, push your catalogue so the AI agent quotes your real prices and stock, and keep customers in step between your site and Barrq.
- Base URL
https://app.barrq.co/api/v1- Format
- JSON request and response bodies, UTF-8
- Transport
- HTTPS only, from your server — never from a browser
- Version
v1— additive changes only
Getting started
Quickstart
- 1
Get your API key
The store owner opens Barrq → Integrations → Website and creates the API key. Barrq shows a Key ID (bq_key_…) and a Secret (bq_secret_…). The Secret is shown once — store it in your server's secret manager. - 2
Test the connection
AGET /orderschanges nothing and returns what the key may do.curl https://app.barrq.co/api/v1/orders \ -H "X-Barrq-Key: bq_key_YOUR_KEY_ID" \ -H "X-Barrq-Secret: bq_secret_YOUR_SECRET" - 3
Report your first order
Send it when the order is placed, then again on every status change.curl -X POST https://app.barrq.co/api/v1/orders \ -H "X-Barrq-Key: bq_key_YOUR_KEY_ID" \ -H "X-Barrq-Secret: bq_secret_YOUR_SECRET" \ -H "Content-Type: application/json" \ -d '{ "order_id": "10045", "status": "pending", "customer": { "name": "Sara Ahmed", "phone": "+201001234567", "email": "sara@example.com", "language": "ar" }, "currency": "EGP", "total": 1250, "items": [ { "name": "Rose face serum", "sku": "SER-01", "qty": 2, "price": 625 } ], "address": "12 Tahrir St, Cairo", "note": "Call before delivery" }'
Getting started
Authentication
Every request carries the Key ID and Secret as two headers. Requests without them, or with a revoked key, return 401.
X-Barrq-Key: bq_key_YOUR_KEY_ID
X-Barrq-Secret: bq_secret_YOUR_SECRETHTTP Basic authentication also works: Authorization: Basic base64(keyId:secret).
Rotating and revoking
If a Secret is lost or exposed, the owner presses Regenerate secret in Barrq. The Key ID stays the same and the old Secret stops working immediately. Revoke switches the key off entirely.
Getting started
Permissions
Reporting orders is always allowed. Catalogue and customer access are off until the store owner enables them in Integrations → Website → Catalogue & customers API. Write access includes read access.
| Setting in Barrq | Scope | Grants |
|---|---|---|
| Catalogue → Read | catalog:read | List and read products |
| Catalogue → Read & write | catalog:write | Create, update and archive products |
| Customers → Read | customers:read | List and read customers |
| Customers → Read & write | customers:write | Create and update customers |
Once your site pushes products, Barrq stops reading the catalogue from your web pages: your API becomes the source of truth.
Sell on your website
Your site as the store
Connect the website as the merchant's store — the same way a Shopify store is connected. From then on the AI agent builds the cart in the chat and sends the customer to your checkout; the order is placed and paid on your site; and the order you report back is tied to the conversation that sold it.
- 1
Push your catalogue
POST /products with SKU, price and stock. The agent only offers what is in stock.
- 2
Choose how customers reach your checkout
A cart link pattern (no code), or a checkout endpoint your server answers.
- 3
The agent sells
It sends a checkout button with your link. It never asks for addresses or payment in chat — your checkout does that.
- 4
Report the order
POST /orders with the barrq_ref from the link. The customer gets WhatsApp updates at every status.
The store owner turns this on in Barrq → Integrations → Website → Sell on your website. A Shopify or WooCommerce connection, if there is one, stays the store.
Sell on your website
Cart link
The simplest way: give Barrq a link pattern your site already understands. Barrq fills it in for each checkout, URL-encoding every value.
https://yourshop.com/cart?add={items}&ref={ref}&lang={locale}| Placeholder | Filled with |
|---|---|
| {items} | Every line as SKU:quantity, comma-separated |
| {sku} / {qty} | The first line only — for sites whose cart link takes one product |
| {ref} | The checkout reference. Keep it with the order and send it back as barrq_ref. |
| {phone} / {name} / {email} | The customer from the chat, to prefill your checkout |
| {locale} | ar or en — the language of the conversation |
Sell on your website
POSTCheckout endpoint
For sites that create the cart on the server. Barrq POSTs the cart to your endpoint, signed, and expects the checkout URL back within 10 seconds. Answer 2xx with checkout_url; total, currency and expires_at are optional. A test: true request comes from the owner's "Try a test checkout" button — do not keep that cart.
POST https://yourshop.com/api/barrq/checkout
Content-Type: application/json
X-Barrq-Event: checkout.create
X-Barrq-Signature: t=1760000000,v1=5f2b…
{
"event": "checkout.create",
"test": false,
"reference": "bq_co_9m7wBFC8y8qq",
"currency": "EGP",
"subtotal": 1250,
"customer": { "name": "Sara Ahmed", "phone": "+201001234567", "email": null, "language": "ar" },
"items": [
{ "sku": "SER-01", "title": "Rose face serum", "quantity": 2, "unit_price": 625 }
]
}Sell on your website
POSTOrder status webhook
Optional, and what lets a merchant run the store from Barrq. When an order your site sent is confirmed, cancelled, shipped or delivered inside Barrq — on its order page, or by the customer on an order-confirmation call — Barrq tells your site, signed. Update the order on your side, then report its new status through POST /orders as usual: reporting the status Barrq already has changes nothing and messages no one, so there is no loop. Answer 2xx; anything else (for example 404 for an order you do not know) is shown on the order in Barrq as a failure.
POST https://yourshop.com/api/barrq/order-status
Content-Type: application/json
X-Barrq-Event: order.status_requested
X-Barrq-Signature: t=1760000000,v1=8c1d…
{
"event": "order.status_requested",
"order_id": "10045",
"order_number": "10045",
"status": "confirmed",
"requested_by": "barrq",
"note": "Customer confirmed on the phone"
}status is one of confirmed, shipped, delivered, cancelled. requested_by is barrq (the merchant, in Barrq) or voice (the customer, on a call); note is optional.
Your site can register this address itself, with the same API key it sends orders with: PUT /api/v1/webhook with { "url": "https://your-shop.com/api/barrq/webhook" }. Call it when your site starts or connects, and nobody has to paste it into Integrations → Website.
Sell on your website
Verifying signatures
Every request Barrq sends to your site carries X-Barrq-Signature: t=<unix seconds>,v1=<hex>, where the hex is HMAC-SHA256 of <t>.<raw body> keyed with the store's signing secret (whsec_…, shown on the Website page). Verify it on the raw body before parsing, and reject requests older than five minutes.
import crypto from "node:crypto";
// rawBody: the exact request body as received (a string), before JSON parsing.
export function verifyBarrq(rawBody, header, secret) {
const t = /t=(\d+)/.exec(header ?? "")?.[1];
const v1 = /v1=([a-f0-9]+)/.exec(header ?? "")?.[1];
if (!t || !v1) return false;
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // 5-minute window
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}Create or update an order, matched by your order_id. Barrq saves it and sends the customer the WhatsApp message that status earns. Sending the same report twice is safe: nothing changes and nobody is messaged twice.
order_idstringrequiredYour order number or id, up to 64 characters.
statusstringrequiredpending · confirmed · paid · shipped · out_for_delivery · delivered · cancelled. Common names from other platforms (processing, completed, refunded…) are mapped automatically.
customer.phonestringoptionalFull number with country code, e.g. +201001234567. Required for WhatsApp messages.
customer.namestringoptionalCustomer name.
customer.emailstringoptionalCustomer email.
customer.languagestringoptionalar or en — the language of the WhatsApp message.
currencystringoptionalISO 4217 code, e.g. EGP.
totalnumberoptionalOrder total.
subtotalnumberoptionalBefore delivery, tax, fees and discounts. Defaults to the items’ total.
delivery_feenumberoptionalDelivery / shipping charge.
taxnumberoptionalTax amount (tax_label optional, default “Tax”).
fees[]arrayoptionalOther charges: { name, amount }.
discountnumberoptionalCheckout discount amount, with discount_label and promo_code (both optional).
discounts[]arrayoptionalMore discounts: { label, amount, code }. Each charge and discount shows as its own line on the order.
items[]arrayoptionalUp to 100: name (required), sku, qty (default 1), price per unit.
tracking_urlstringoptionalhttps:// link, sent with the shipped update.
addressstringoptionalDelivery address, up to 1,000 characters.
notestringoptionalUp to 1,000 characters.
barrq_refstringoptionalThe {ref} of the checkout the agent sent. Ties the order to the conversation and customer that produced it.
curl -X POST https://app.barrq.co/api/v1/orders \
-H "X-Barrq-Key: bq_key_YOUR_KEY_ID" \
-H "X-Barrq-Secret: bq_secret_YOUR_SECRET" \
-H "Content-Type: application/json" \
-d '{ "order_id": "10045", "status": "pending", "customer": { "name": "Sara Ahmed", "phone": "+201001234567", "email": "sara@example.com", "language": "ar" }, "currency": "EGP", "total": 1250, "items": [ { "name": "Rose face serum", "sku": "SER-01", "qty": 2, "price": 625 } ], "address": "12 Tahrir St, Cairo", "note": "Call before delivery" }'{
"ok": true,
"order": { "id": "cm2x8k…", "order_number": "10045", "status": "pending", "created": true },
"whatsapp": { "sent": "text" }
}whatsapp.sent is text or template when a message went out, otherwise none with a reason. The order is saved either way.
| reason | Meaning |
|---|---|
no_change | The status did not change — nothing new to tell the customer. |
no_phone | No usable customer phone number. |
stage_off | The store switched off messages for this status. |
template_pending | Outside WhatsApp’s 24-hour window and the message template is not approved yet. |
send_failed | WhatsApp refused the message; detail says why. |
Verifies the key without changing anything. Use it behind a "Test connection" button in your settings screen.
{
"ok": true,
"statuses": ["pending", "confirmed", "paid", "shipped", "out_for_delivery", "delivered", "cancelled"],
"access": ["orders", "catalog:read"]
}Up to 100 products per request, matched by your sku. A new SKU needs title and price; for an existing SKU send only what changed. If any product is invalid, nothing is saved and you get 422 with every problem in details.
skustringrequiredYour stable id for the product, up to 190 characters.
title / title_arstringoptionalName in English / Arabic. title is required for a new product.
description / description_arstringoptionalUp to 5,000 characters each.
pricenumberoptional≥ 0. Required for a new product.
compare_at_pricenumber | nulloptionalThe "was" price; kept only when higher than price. null clears it.
currencystringoptional3-letter code. Defaults to the store currency.
stockintegeroptional≥ 0. 0 = out of stock; going above 0 sends back-in-stock alerts.
categorystringoptionalFree text.
image_url / urlstringoptionalhttp(s) links. url is the product page the agent shares.
activebooleanoptionalfalse takes the product off sale; true restores it.
curl -X POST https://app.barrq.co/api/v1/products \
-H "X-Barrq-Key: bq_key_YOUR_KEY_ID" \
-H "X-Barrq-Secret: bq_secret_YOUR_SECRET" \
-H "Content-Type: application/json" \
-d '{ "products": [
{ "sku": "SER-01", "title": "Rose face serum", "title_ar": "سيروم الورد",
"price": 625, "compare_at_price": 750, "currency": "EGP", "stock": 40,
"category": "Skincare", "image_url": "https://example.com/ser-01.jpg",
"url": "https://example.com/p/ser-01" },
{ "sku": "SOAP-01", "stock": 0 }
] }'{
"ok": true,
"created": 1,
"updated": 1,
"results": [
{ "sku": "SER-01", "id": "cm2x…", "status": "created" },
{ "sku": "SOAP-01", "id": "cm2y…", "status": "updated" }
]
}The catalogue, most recently changed first. See Pagination.
pageintegeroptionalFrom 1. Default 1.
limitintegeroptional1–100. Default 50.
updated_sinceISO 8601optionalOnly products changed since then — for incremental syncs.
skustringoptionalReturn one product.
include_inactivebooleanoptionaltrue includes archived products.
Each product: id, sku, title, title_ar, description, description_ar, price, compare_at_price, currency, stock, in_stock, category, image_url, url, active, source, updated_at.
One product by your SKU (or Barrq's id). 404 when there is none.
Takes the product off sale. It is archived, not erased — past orders and conversations still refer to it. POST it again with "active": true to restore it.
Up to 100 per request, matched by phone first, then email. Only the fields sent are changed; tags are added, never removed.
phonestringoptionalWith country code, e.g. +971501234567. phone or email is required.
emailstringoptionalphone or email is required.
namestringoptionalUp to 190 characters.
tagsstring[]optionalUp to 30 tags, 60 characters each.
marketing_opt_inbooleanoptionaltrue = consented to marketing; false = opted out. Omit to keep the current choice.
curl -X POST https://app.barrq.co/api/v1/customers \
-H "X-Barrq-Key: bq_key_YOUR_KEY_ID" \
-H "X-Barrq-Secret: bq_secret_YOUR_SECRET" \
-H "Content-Type: application/json" \
-d '{ "customers": [
{ "phone": "+971501234567", "name": "Omar Khalid", "email": "omar@example.com",
"tags": ["vip", "newsletter"], "marketing_opt_in": true }
] }'Customers from every channel Barrq serves, most recently active first.
page / limitintegeroptionalAs for products.
updated_sinceISO 8601optionalOnly customers changed since then.
phone / emailstringoptionalFind one person.
Each customer: id, name, phone, email, whatsapp, instagram, status, tags, orders_count, lifetime_value, marketing_opt_in, first_seen_at, last_seen_at.
One customer by Barrq's id. 404 when there is none.
Reference
Pagination
List endpoints take page and limit and return the total. Request the next page while has_more is true. For scheduled syncs, store the time of your last run and send it as updated_since.
{ "ok": true, "products": [ … ], "page": 1, "limit": 100, "total": 342, "has_more": true }Reference
Errors
Errors are JSON with a message written for your logs, and a list of every problem when a body is invalid.
{
"ok": false,
"error": "Some products could not be read. Nothing was saved.",
"details": ["products[1] (SOAP-01): \"price\" must be a number ≥ 0."]
}| Status | Meaning | What to do |
|---|---|---|
| 200 / 201 | Success (201 = a new order) | — |
| 400 | Body is not JSON, or bad page / limit / updated_since | Fix the request |
| 401 | Missing, wrong or revoked credentials | Check the Key ID and current Secret |
| 403 | Integration disabled, or the key lacks the scope | The owner enables the permission |
| 404 | No product or customer with that identifier | Check the SKU or id |
| 413 | More than 100 items in one request | Send batches of up to 100 |
| 422 | Invalid fields — listed in details | Fix them; nothing was saved |
| 429 | Rate limit reached | Wait Retry-After seconds, then retry |
| 500 | Error on Barrq’s side | Retry with the same body — it is safe |
Reference
Rate limits & retries
Each key may make 120 requests per minute. Repeated failed authentication from one address is throttled too. A 429 carries a Retry-After header in seconds.
Every write is idempotent: orders are matched by order_id, products by sku, customers by phone or email. Retry on 429, 5xx and network errors with exponential back-off (for example 1s, 2s, 4s, up to 5 attempts).
Reference
Security
Encrypted in transit
Call the API over HTTPS only, so the key and every payload are encrypted on the wire. Never send the Secret over plain HTTP.
Secrets never stored
Barrq keeps only a SHA-256 hash of each Secret and compares it in constant time. 40 random characters — not guessable.
Tenant isolation
A key reaches its own store’s data and nothing else. Catalogue and customer scopes are off by default.
Instant rotation
Regenerate or revoke a key at any time; the old Secret stops working immediately.
Your part
Keep the Secret on your server — in environment variables or a secret manager — never in front-end code, mobile apps or a public repository. If it is ever exposed, regenerate it straight away. More in our security overview.
Reference
Versioning
The API is versioned in the path. Within v1 we only add — new endpoints, new optional fields, new response fields — so ignore fields you do not recognise. A breaking change would ship as v2, with v1 kept running and notice given well in advance.
Reference
Go-live checklist
- ✓The store owner created the API key and shared the Key ID and Secret privately.
- ✓Both values live in your server’s secrets, not in front-end code.
- ✓GET /orders returns ok: true with the access you expect.
- ✓Catalogue and/or Customers access is enabled, if you sync them.
- ✓Your site calls POST /orders on every new order and every status change.
- ✓Customer phones are sent in international format (+ and country code).
- ✓Bulk syncs use batches of up to 100 with retries on 429 and 5xx.
- ✓A test order to your own WhatsApp number produced a message for each status.
Building something bigger?
Enterprise plans come with a dedicated account manager and an SLA. For support, send us the time of the request and the error text from the response.