Developer API documentation
Connect your own website to AshalNow: receive orders from your site directly into the same branch order list, and read products, inventory, and branches live. Keys are issued by the AshalNow team — contact us to get one for your account.
Quick start
All requests are sent to https://www.ashalnow.com/api/v1, and every request must carry this authorization header:
Authorization: Bearer <keyId>:<secret>If your key has allowed domains configured, the Origin header must match one of them or the request is rejected with a 403. Every successful response has this shape:
{ "success": true, "data": ..., "meta": { "timestamp": "..." } }And any failure has this shape:
{ "success": false, "error": { "code": "NOT_FOUND", "message": "Product not found" } }Rate limits
Every key has a default limit of 60 requests/minute and 5000 requests/day (adjustable by our team as you need). Exceeding it returns 429 RATE_LIMITED.
Endpoints
/productsRequires permission: products:readList of products. Optional filters: page, limit, category, search, inStock, minPrice, maxPrice, sort (name|-name|price|-price|newest).
curl "https://www.ashalnow.com/api/v1/products?inStock=true&limit=20" \
-H "Authorization: Bearer ak_xxx:xxxxxx"
{
"success": true,
"data": [
{ "id": "...", "name": "Cola 330ml", "barcode": "...", "categoryName": "Drinks",
"sellingPrice": 15, "quantity": 42, "imageUrl": null }
],
"meta": { "page": 1, "limit": 20, "total": 118, "timestamp": "2026-07-25T10:00:00.000Z" }
}/products/:idRequires permission: products:readData for a single product.
curl https://www.ashalnow.com/api/v1/products/PRODUCT_ID \
-H "Authorization: Bearer ak_xxx:xxxxxx"/products/:id/stockRequires permission: inventory:readStock quantity for a specific product. Add ?branchId=... for a single branch's quantity, otherwise the total across all branches is returned.
curl "https://www.ashalnow.com/api/v1/products/PRODUCT_ID/stock?branchId=BRANCH_ID" \
-H "Authorization: Bearer ak_xxx:xxxxxx"/categoriesRequires permission: products:readAll categories.
curl https://www.ashalnow.com/api/v1/categories \
-H "Authorization: Bearer ak_xxx:xxxxxx"/branchesRequires permission: branches:readActive branches only. If the store has no branches, the list comes back empty — an order in that case needs neither branchId nor area. serviceAreas are the same areas used to auto-determine the branch when creating an order with customer.area.
curl https://www.ashalnow.com/api/v1/branches \
-H "Authorization: Bearer ak_xxx:xxxxxx"
{ "success": true, "data": [
{ "id": "...", "name": "Nasr City branch", "address": "...", "phone": "...",
"acceptsPickup": true, "acceptsDelivery": true,
"serviceAreas": ["Nasr City", "Fifth Settlement"] }
] }/inventoryRequires permission: inventory:readStock quantity for all products at once. Add ?branchId=... for a single branch's quantities.
curl "https://www.ashalnow.com/api/v1/inventory?branchId=BRANCH_ID" \
-H "Authorization: Bearer ak_xxx:xxxxxx"/inventory/:productIdRequires permission: inventory:readSame logic as /products/:id/stock — an equivalent alternative.
curl https://www.ashalnow.com/api/v1/inventory/PRODUCT_ID \
-H "Authorization: Bearer ak_xxx:xxxxxx"/ordersRequires permission: orders:writeCreate a new order from your site. The customer is matched automatically by phone number (updated if it already exists, created as a new record if not). Quantities are checked live against available stock but only deducted once the order is fulfilled at the point of sale. If the store has branches, you need one of: branchId (if you already know the branch), or customer.area (the branch is auto-determined by whichever branch covers that area — the order is rejected with a clear message if no branch covers it, or more than one does).
curl -X POST https://www.ashalnow.com/api/v1/orders \
-H "Authorization: Bearer ak_xxx:xxxxxx" \
-H "Content-Type: application/json" \
-d '{
"customer": {
"name": "Ahmed Mohamed", "phone": "01012345678",
"address": "6th of October, District 1", "area": "Nasr City"
},
"items": [ { "productId": "PRODUCT_ID", "quantity": 2 } ],
"deliveryMethod": "delivery",
"notes": "Please call before delivery"
}'
// branchId is optional here — if the store has branches it's determined automatically from "area"
{ "success": true, "data": {
"id": "...", "status": "pending",
"customer": { "id": "...", "name": "Ahmed Mohamed", "phone": "01012345678" },
"items": [ { "productId": "...", "productName": "...", "quantity": 2 } ],
"branchId": "...", "deliveryMethod": "delivery", "createdAt": "..."
} }/ordersRequires permission: orders:readOnly orders created by this key (orders are never shared between different keys). Filters: page, limit, status.
curl "https://www.ashalnow.com/api/v1/orders?status=pending" \
-H "Authorization: Bearer ak_xxx:xxxxxx"/orders/:idRequires permission: orders:readDetails of a single order created with this same key, including its current status (pending/fulfilled/cancelled).
curl https://www.ashalnow.com/api/v1/orders/ORDER_ID \
-H "Authorization: Bearer ak_xxx:xxxxxx"/webhooks/testSends a synthetic test.ping event to the webhook URL registered on your key — to confirm the signature and receiving side work before relying on them.
curl -X POST https://www.ashalnow.com/api/v1/webhooks/test \
-H "Authorization: Bearer ak_xxx:xxxxxx"Error codes
| Code | HTTP | Meaning |
|---|---|---|
| UNAUTHORIZED | 401 | The key is invalid, or the format is wrong (must be Authorization: Bearer keyId:secret). |
| RATE_LIMITED | 429 | You exceeded this key's per-minute or per-day request limit. |
| NOT_FOUND | 404 | The path or resource (product/order) does not exist. |
| ERROR | 400/403/500 | A validation error, a missing permission, or an internal error — the message field explains the cause in detail. |
Webhooks
If you registered a webhook URL on your key, we'll send a POST for every one of these events:
- order.created
- order.delivered
- order.cancelled
- inventory.low
- inventory.out
- product.updated
- product.disabled
Payload shape:
{
"event": "order.created",
"apiVersion": "v1",
"timestamp": "2026-07-25T10:00:00.000Z",
"clientId": "ak_xxx",
"data": { ... }
}Every request carries the header X-AshalNow-Signature in the form sha256=<hex> — an HMAC-SHA256 of the raw body text (before any JSON.parse) using your key's webhook secret. Verify the signature before trusting any event:
const crypto = require("crypto");
const expected = "sha256=" + crypto
.createHmac("sha256", webhookSecret)
.update(rawBody) // raw string, not parsed JSON
.digest("hex");
const valid = expected === receivedSignatureHeader;If your server returns anything other than 2xx, or doesn't respond within 10 seconds, we retry automatically at increasing intervals (1 minute, 5 minutes, 30 minutes, 2 hours, 24 hours) before marking delivery as permanently failed.
Ready-made SDK (JavaScript / Node.js)
A lightweight client with no external dependencies — use it from your own server (not from the customer's browser, so your secret never leaks).
Download ashalnow.jsconst AshalNow = require("./ashalnow.js");
const client = new AshalNow({ keyId: "ak_xxx", secret: "xxxxxx" });
const products = await client.listProducts({ inStock: true });
const order = await client.createOrder({
customer: { name: "Ahmed Mohamed", phone: "01012345678", address: "..." },
items: [{ productId: "PRODUCT_ID", quantity: 2 }],
deliveryMethod: "delivery",
});