Developer API
Send orders from your own website
Many sellers run a shop site on their own brand domain. The Order Capture API lets that site, or any other portal, place its orders straight into your Crackers Billing account: one POST request, and the order shows up on your Orders screen with the same WhatsApp alert and tracking link as an order from your public order page.
1. Create an API key
In the app, open Settings → Developers, give the key a name (for example “Brand website”) and press Create key. Copy the key immediately: it starts with cbk_ and is shown only once. Keep it on your server, never in browser code, and revoke it from the same screen if it ever leaks. You can hold up to 10 active keys, one per integration.
2. Call the endpoint
Orders go to a single endpoint. Authenticate with your key as a Bearer token; the key tells us which account the order belongs to.
POST https://crackersbillingsoftware.com/api/v1/orders Authorization: Bearer cbk_YOUR_KEY Content-Type: application/json
A complete request with curl:
curl -X POST https://crackersbillingsoftware.com/api/v1/orders \
-H "Authorization: Bearer cbk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer_name": "Kumar",
"customer_phone": "9876543210",
"customer_address": "12 Main Street, Chennai",
"customer_pincode": "600001",
"transport_preference": "delivery",
"note": "From brand site. Ref #WEB-1042",
"advance_note": "Paid Rs 500 advance by UPI",
"pricing_version": "Rate A",
"gst_percent": 18,
"packaging_percent": 3,
"discount_percent": 10,
"items": [
{ "product_id": 105, "name": "Flower Pots Big", "quantity": 2,
"price": 240, "offer_price": 270, "list_price": 300 },
{ "serial_no": "12", "name": "Ground Chakkar", "quantity": 5, "price": 80 }
]
}'The same call from a Node.js or serverless backend:
const res = await fetch("https://crackersbillingsoftware.com/api/v1/orders", {
method: "POST",
headers: {
"Authorization": "Bearer " + process.env.CRACKERS_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
customer_name: form.name,
customer_phone: form.phone,
customer_address: form.address,
customer_pincode: form.pincode,
gst_percent: 18,
items: cart.map((line) => ({
name: line.name,
quantity: line.qty,
price: line.price, // final rate after your discounts
list_price: line.mrp, // optional MRP anchor
})),
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.order.code); // "ORD-7XKQ4M"
console.log(data.order.track_url); // show this to your buyerProducts, price lists and matching
A product name alone is not enough to identify a product: the same item can sit in several retail price lists (“Rate A”, “Rate B”) at different rates, and names repeat. Lines are matched to your catalogue in this order:
- product_id: the exact id from your catalogue. Best option; sync ids with the products endpoint below.
- serial_no within pricing_version: your pamphlet number. Used when there is no product_id and exactly one product carries that serial in the chosen list.
- Neither: the line is recorded as free text with the name and prices you sent. The order is still complete; the line just is not connected to a catalogue product, so it will not appear in product-wise stock and insights.
Fetch your catalogue, with ids, serials, current rates and MRP, using the same key. The response also names your price lists; pass one as ?pricing_version= when you have more than one.
curl "https://crackersbillingsoftware.com/api/v1/products?pricing_version=Rate%20A" \
-H "Authorization: Bearer cbk_YOUR_KEY"
{
"pricing_versions": ["Rate A", "Rate B"],
"pricing_version": "Rate A",
"products": [
{ "id": 105, "serial_no": "1", "name": "Flower Pots Big", "unit": "box",
"price": 270, "mrp": 300, "pieces_per_case": "10.00",
"category": "Flower Pots", "public_visible": true }
]
}Request fields
| Field | Type | Required | Notes |
|---|---|---|---|
| customer_name | string | Yes | Buyer's name, as it should appear on the order. |
| customer_phone | string | Yes | 7 to 15 digits, optional leading +. Spaces are ignored. |
| customer_address | string | No | Delivery or contact address. |
| customer_pincode | string | No | 6-digit PIN code. Stored on its own line inside the address, the same way the public order page records it. |
| transport_preference | string | No | pickup or delivery. |
| note | string | No | Anything the seller should see, such as your own order reference. |
| advance_note | string | No | Advance or payment note, for example Paid Rs 500 by UPI. |
| pricing_version | string | No | Name of the retail price list this cart was built from, for example Rate A. Needed for serial_no matching; stamped on the order. |
| gst_percent | number | No | 0 to 100, default 0. GST added on the item subtotal. |
| packaging_percent | number | No | 0 to 100, default 0. Packaging charge added on the item subtotal. |
| discount_percent | number | No | 0 to 100, default 0. Records the flat additional discount your site already applied to the line prices. Informational: it labels the discount on documents, the amounts come from the price anchors below. |
| items | array | Yes | 1 to 500 lines. Each line needs name, quantity and price. |
| items[].name | string | Yes | Product name printed on the order. |
| items[].quantity | number | Yes | Greater than 0. Fractions are allowed. |
| items[].price | number | Yes | Final unit price in rupees after every discount, 0 or more. Taken as sent, never re-priced. |
| items[].product_id | number | No | Id of one of your products, from GET /api/v1/products. The strongest match; ids from other accounts are ignored. |
| items[].serial_no | string | No | Your pamphlet serial number, matched within pricing_version. Used only when product_id is absent and the serial is unambiguous. |
| items[].offer_price | number | No | Your rate before the flat additional discount. Splits the buyer's savings into product discount and additional discount. |
| items[].list_price | number | No | MRP anchor. When higher than price, documents show the struck-through discount. |
Line prices are yours: send the final per-unit rate the buyer agreed to pay, and the API records it without re-pricing. The only amounts computed on our side are the order-level charges you ask for: total = subtotal + packaging_percent% + gst_percent%, both percentages applied on the item subtotal, rounded to the paise. In the curl example above, Flower Pots Big carries MRP 300, your rate 270 and a flat 10% off MRP bringing it to 240: the response splits that into an item discount of 60 and an additional discount of 60, then adds 3% packaging and 18% GST on the 880 subtotal for a total of 1064.80.
3. Read the response
Success returns 201 with the order as recorded. Show order.code to your buyer as the confirmation number, and order.track_url as their live tracking page.
{
"order": {
"code": "ORD-7XKQ4M",
"status": "new",
"item_count": 2,
"subtotal": 880.00,
"item_discount": 60.00,
"additional_discount": 60.00,
"additional_discount_percent": 10,
"packaging_charge": 26.40,
"gst_amount": 158.40,
"total": 1064.80,
"track_url": "https://crackersbillingsoftware.com/track/abc123...",
"created_at": "2026-08-29T10:15:00.000Z"
}
}Errors
Every error is JSON with a single { "error": "message" } body.
| Status | When |
|---|---|
| 401 | Missing or invalid Authorization header, unknown or revoked key. |
| 400 | Validation failed. The error message names the exact field, such as items[0].price. |
| 429 | Over 60 requests per minute for this key. Wait and retry. |
| 500 | Unexpected error on our side. Retrying may create a duplicate order; the seller can cancel it. |
Good to know
- Rate limit: 60 requests per minute per key. Plenty for order traffic; batch imports should pace themselves.
- There is no idempotency key yet. If a request times out and you retry, you may create a duplicate order; the seller cancels it like any other duplicate enquiry.
- API orders appear on the Orders screen with an API badge, and convert to quotations and bills exactly like online orders.
- Revoking a key takes effect immediately. Rotate keys by creating a new one first, switching your site over, then revoking the old one.
- Need order status or webhooks? Tell us what you are building; the API grows with real integrations.