Create sales, contacts and payments in a NegoBill business from your own software. This page and https://api.negobill.com/openapi.json (OpenAPI 3.1) are generated from the same source and describe exactly what the server does.
https://api.negobill.com/v1. The test environment is https://test.api.negobill.com/v1 — a separate installation with its own businesses and its own keys (a key made there starts with nbk_test_; a production key with nbk_live_). Nothing done in test touches production.64000). Times are ISO 8601 UTC (2026-09-18T09:31:12.000Z); days are YYYY-MM-DD in the store's timezone. Ids are 24 hex characters.curl -u "nbk_live_Ab3dEf6hIj9kLm2n:nbs_9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a" \ https://api.negobill.com/v1/meA
200 with "status": true and your business name means the setup is right. Any other answer: see the error table.POST /contacts), then the sale (POST /sales) with its id, then record the payment (POST /sales/{id}/payments).HTTP Basic: the Key ID as the username and the Key Secret as the password, on every call.
Authorization: Basic base64("nbk_live_Ab3dEf6hIj9kLm2n:nbs_9f8e7d6c…")
A key carries scopes; each endpoint below names the one it needs. A key without it gets 403 NB-B-API-1004. Scopes: contacts:read (Look up customers and suppliers.), contacts:write (Add customers your software already knows.), products:read (Look up products, prices and stock left.), sales:read (Read a sale and what is still due on it.), sales:write (Make sales — NegoBill computes GST, totals and stock.), payments:read (Read the receipts recorded against a sale.), payments:write (Record money received against a sale.).
Keys do not expire. The owner can revoke a key (it stops at once), regenerate its secret (the old secret keeps working for the overlap the owner chose, 24 hours by default), and restrict it to an IP allowlist (403 NB-B-API-1008 from anywhere else).
Every JSON answer has this shape. correlationId is also returned as the x-correlation-id header — quote it when asking for help.
{ "status": true, "statusCode": 200, "correlationId": "2f6c1e0a9b3d4", "results": { … } }
{ "status": false, "statusCode": 400, "correlationId": "2f6c1e0a9b3d4", "code": "NB-B-API-1000", "message": "Validation failed. See details.", "details": [ { "path": "lines.0.quantity", "message": "…" } ] }
Send Idempotency-Key: <a unique string, ≤80 characters> on any POST. The same key with the same body returns the stored answer (with the header Idempotent-Replayed: true); the same key with a different body is refused with 409 NB-B-API-1006; a key whose first call is still running answers 409 too — wait a second and retry. Keys are remembered for 7 days. A failed first call (4xx/5xx) does not consume the key. Independently of this header, POST /contacts, POST /sales and POST /sales/{id}/payments return the existing record when your reference already exists, so a retry never duplicates a customer, a bill or a receipt.
120 requests a minute per key. Every answer carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds). Past the limit: 429 NB-B-API-1005 with Retry-After (seconds) — wait that long, then retry. Lookups return at most 50 rows and are for looking things up, not for copying a catalogue: keep your own copy of your products and customers. Sales made through the API count in the plan's monthly sale quota (403 NB-B-PLN-1006 when it is reached).
GET https://api.negobill.com/v1/meWho am I. Confirms the credentials and tells you which business, store and billing profile this key acts for. The quickstart call.
Scope: contacts:read · Header: Authorization: Basic <base64(keyId:keySecret)>
The envelope (below) with results of:
| Field | Type | Required | Notes |
|---|---|---|---|
business | object | no | |
id | string, 24 hex characters | no | |
name | string | no | |
store | object | no | The store every sale made with this key belongs to. |
id | string, 24 hex characters | no | |
name | string | no | |
timezone | string | no | IANA zone, e.g. Asia/Kolkata. |
billingProfile | object | no | |
id | string, 24 hex characters | no | |
legalName | string | null | no | |
gstNumber | string | null | no | When null the business issues BILLs only; INVOICE is refused. |
plan | object | no | |
code | string | no | STARTER, STANDARD or PRO. |
label | string | no | |
key | object | no | |
keyId | string | no | |
label | string | no | |
scopes | string[] | no | |
environment | string | no | LIVE or TEST. |
{
"status": true,
"statusCode": 200,
"correlationId": "2f6c1e0a9b3d4",
"results": {
"business": {
"id": "66f1a2b3c4d5e6f7a8b9c0a0",
"name": "Kiran Super Mart"
},
"store": {
"id": "66f1a2b3c4d5e6f7a8b9c0a1",
"name": "Main store",
"timezone": "Asia/Kolkata"
},
"billingProfile": {
"id": "66f1a2b3c4d5e6f7a8b9c0a2",
"legalName": "Kiran Super Mart",
"gstNumber": "33AAAAA0000A1Z5"
},
"plan": {
"code": "BUSINESS",
"label": "Business"
},
"key": {
"keyId": "nbk_live_Ab3dEf6hIj9kLm2n",
"label": "Shopify connector",
"scopes": [
"contacts:read",
"sales:write"
],
"environment": "LIVE"
}
}
}
POST https://api.negobill.com/v1/contactsFind or create a contact. Looks for an existing contact by reference, then by gstNumber, then by phone; a match is returned with created:false (and takes your reference if it had none). Otherwise the contact is created and returned with created:true. Phone and GST number are unique within a business, which is why matching comes first.
Scope: contacts:write · Header: Authorization: Basic <base64(keyId:keySecret)> · Optional header: Idempotency-Key: <your unique key, ≤80 chars> · Content-Type: application/json
| Field | Type | Required | Notes |
|---|---|---|---|
reference | string | no | Your own id for this contact, 1–80 characters. Unique within the business. |
name | string | no | Required unless phone is given. Max 300. |
businessName | string | no | Max 500. |
phone | string | no | Digits only, national number without the country code, 4–15 digits. Required unless name is given. |
phoneCountryCode | string | no | Digits without +, default the store country's (India: "91"). |
email | string | no | Max 200. |
gstNumber | string | no | 15-character GSTIN. Its first two digits are the state code; a contradicting address.stateCode is refused. |
roles | string[] | no | Each CUSTOMER or SUPPLIER. Default ["CUSTOMER"]. |
address | object | no | |
line1 | string | no | |
line2 | string | no | |
city | string | no | |
district | string | no | |
state | string | no | |
stateCode | string | no | 2 digits. |
pincode | string | no | 6 digits. |
note | string | no | Max 300. |
{
"reference": "cust-1001",
"name": "Asha Stores",
"phone": "9876543210",
"email": "asha@example.com",
"gstNumber": "33ABCDE1234F1Z5",
"address": {
"line1": "12 Market Road",
"city": "Chennai",
"state": "Tamil Nadu",
"pincode": "600001"
}
}
The envelope (below) with results of:
| Field | Type | Required | Notes |
|---|---|---|---|
id | string, 24 hex characters | no | The contact id. Use it as contactId on a sale. |
name | string | null | no | |
businessName | string | null | no | Registered name, if a business. |
phone | string | null | no | Digits only, national number. |
phoneCountryCode | string | null | no | Dialling code without +, e.g. "91". |
email | string | null | no | |
gstNumber | string | null | no | 15-character GSTIN. |
roles | string[] | no | Each one of CUSTOMER, SUPPLIER. |
address | object | no | Every key present, null when unknown. |
line1 | string | null | no | |
line2 | string | null | no | |
city | string | null | no | |
district | string | null | no | |
state | string | null | no | |
stateCode | string | null | no | GST state code, 2 digits. |
pincode | string | null | no | 6 digits. |
reference | string | null | no | Your own id for this contact, if you gave one. |
status | string | no | ACTIVE or INACTIVE. |
createdAt | string | no | ISO 8601 UTC. |
created | boolean | no | true when this call created the contact. |
{
"status": true,
"statusCode": 201,
"correlationId": "2f6c1e0a9b3d4",
"results": {
"id": "66f1a2b3c4d5e6f7a8b9c0d1",
"name": "Asha Stores",
"businessName": null,
"phone": "9876543210",
"phoneCountryCode": "91",
"email": "asha@example.com",
"gstNumber": "33ABCDE1234F1Z5",
"roles": [
"CUSTOMER"
],
"address": {
"line1": "12 Market Road",
"line2": null,
"city": "Chennai",
"district": null,
"state": "Tamil Nadu",
"stateCode": "33",
"pincode": "600001"
},
"reference": "cust-1001",
"status": "ACTIVE",
"createdAt": "2026-09-18T09:30:00.000Z",
"created": true
}
}
GET https://api.negobill.com/v1/contactsLook up contacts. Exact lookups by reference, phone or gstNumber (any combination, all must match), or a word search. At most 50 rows; this is a lookup, not a way to copy the contact list.
Scope: contacts:read · Header: Authorization: Basic <base64(keyId:keySecret)>
| Parameter | Type | Required | Notes |
|---|---|---|---|
reference | string | no | |
phone | string | no | Digits only. |
gstNumber | string | no | |
search | string | no | Any word of the name, nickname, business or trade name; digits match the start of the phone. Ignored when an exact key is given. |
limit | integer | no | 1–50, default 20. |
The envelope (below) with results of:
| Field | Type | Required | Notes |
|---|---|---|---|
contacts | object[] | no | |
id | string, 24 hex characters | no | The contact id. Use it as contactId on a sale. |
name | string | null | no | |
businessName | string | null | no | Registered name, if a business. |
phone | string | null | no | Digits only, national number. |
phoneCountryCode | string | null | no | Dialling code without +, e.g. "91". |
email | string | null | no | |
gstNumber | string | null | no | 15-character GSTIN. |
roles | string[] | no | Each one of CUSTOMER, SUPPLIER. |
address | object | no | Every key present, null when unknown. |
line1 | string | null | no | |
line2 | string | null | no | |
city | string | null | no | |
district | string | null | no | |
state | string | null | no | |
stateCode | string | null | no | GST state code, 2 digits. |
pincode | string | null | no | 6 digits. |
reference | string | null | no | Your own id for this contact, if you gave one. |
status | string | no | ACTIVE or INACTIVE. |
createdAt | string | no | ISO 8601 UTC. |
{
"status": true,
"statusCode": 200,
"correlationId": "2f6c1e0a9b3d4",
"results": {
"contacts": [
{
"id": "66f1a2b3c4d5e6f7a8b9c0d1",
"name": "Asha Stores",
"businessName": null,
"phone": "9876543210",
"phoneCountryCode": "91",
"email": "asha@example.com",
"gstNumber": "33ABCDE1234F1Z5",
"roles": [
"CUSTOMER"
],
"address": {
"line1": "12 Market Road",
"line2": null,
"city": "Chennai",
"district": null,
"state": "Tamil Nadu",
"stateCode": "33",
"pincode": "600001"
},
"reference": "cust-1001",
"status": "ACTIVE",
"createdAt": "2026-09-18T09:30:00.000Z"
}
]
}
}
GET https://api.negobill.com/v1/contacts/{id}Read a contact.
Scope: contacts:read · Header: Authorization: Basic <base64(keyId:keySecret)>
| Parameter | Type | Required | Notes |
|---|---|---|---|
id | string, 24 hex characters | yes |
The envelope (below) with results of:
| Field | Type | Required | Notes |
|---|---|---|---|
id | string, 24 hex characters | no | The contact id. Use it as contactId on a sale. |
name | string | null | no | |
businessName | string | null | no | Registered name, if a business. |
phone | string | null | no | Digits only, national number. |
phoneCountryCode | string | null | no | Dialling code without +, e.g. "91". |
email | string | null | no | |
gstNumber | string | null | no | 15-character GSTIN. |
roles | string[] | no | Each one of CUSTOMER, SUPPLIER. |
address | object | no | Every key present, null when unknown. |
line1 | string | null | no | |
line2 | string | null | no | |
city | string | null | no | |
district | string | null | no | |
state | string | null | no | |
stateCode | string | null | no | GST state code, 2 digits. |
pincode | string | null | no | 6 digits. |
reference | string | null | no | Your own id for this contact, if you gave one. |
status | string | no | ACTIVE or INACTIVE. |
createdAt | string | no | ISO 8601 UTC. |
{
"status": true,
"statusCode": 200,
"correlationId": "2f6c1e0a9b3d4",
"results": {
"id": "66f1a2b3c4d5e6f7a8b9c0d1",
"name": "Asha Stores",
"businessName": null,
"phone": "9876543210",
"phoneCountryCode": "91",
"email": "asha@example.com",
"gstNumber": "33ABCDE1234F1Z5",
"roles": [
"CUSTOMER"
],
"address": {
"line1": "12 Market Road",
"line2": null,
"city": "Chennai",
"district": null,
"state": "Tamil Nadu",
"stateCode": "33",
"pincode": "600001"
},
"reference": "cust-1001",
"status": "ACTIVE",
"createdAt": "2026-09-18T09:30:00.000Z"
}
}
GET https://api.negobill.com/v1/productsLook up products. An exact code (barcode) lookup, or a word search; at most 50 rows with the stock left at the key's store. Keep your own copy of your catalogue — this call is for looking a product up, not for serving your storefront.
Scope: products:read · Header: Authorization: Basic <base64(keyId:keySecret)>
| Parameter | Type | Required | Notes |
|---|---|---|---|
code | string | no | Exact product code / barcode. When given, search is ignored. |
search | string | no | Any word of the name or search tag; a prefix of each word matches. |
limit | integer | no | 1–50, default 20. |
The envelope (below) with results of:
| Field | Type | Required | Notes |
|---|---|---|---|
products | object[] | no | |
id | string, 24 hex characters | no | |
name | string | no | |
code | string | null | no | Barcode / product code. |
hsnSac | string | null | no | |
taxPercent | number | no | One of 0, 5, 12, 18, 24, 28, 40. |
mrp | integer | no | Paise. |
salePrice | integer | no | Paise, GST-inclusive retail price. |
wholesalePrice | integer | null | no | Paise. |
unit | string | null | no | Primary unit code, e.g. "PCS", "KG". |
isStockTracked | boolean | no | |
stockLeft | number | null | no | Units left at the key's store; null when the product is not stock-tracked. |
status | string | no | ACTIVE or INACTIVE. |
batches | object[] | no | Only on GET /products/{id}, only when the store sells by batch: the batches with stock, oldest first. Name one on a sale line as batchId, or leave it out and the oldest is taken. |
id | string, 24 hex characters | no | |
batchNo | string | null | no | |
stockLeft | number | no | Units left in this batch. |
mrp | integer | null | no | Paise, this batch's printed MRP. |
salePrice | integer | null | no | Paise, this batch's price. |
expiryDate | string | null | no | YYYY-MM-DD. |
{
"status": true,
"statusCode": 200,
"correlationId": "2f6c1e0a9b3d4",
"results": {
"products": [
{
"id": "66f1a2b3c4d5e6f7a8b9c0f3",
"name": "Aashirvaad Atta 5 kg",
"code": "8901058000123",
"hsnSac": "1101",
"taxPercent": 5,
"mrp": 31000,
"salePrice": 29500,
"wholesalePrice": 28000,
"unit": "PCS",
"isStockTracked": true,
"stockLeft": 28,
"status": "ACTIVE",
"batches": [
{
"id": "66f1a2b3c4d5e6f7a8b9c0b7",
"batchNo": "B-0912",
"stockLeft": 28,
"mrp": 31000,
"salePrice": 29500,
"expiryDate": "2027-03-31"
}
]
}
]
}
}
GET https://api.negobill.com/v1/products/{id}Read a product. One product with its prices and the stock left at the key's store.
Scope: products:read · Header: Authorization: Basic <base64(keyId:keySecret)>
| Parameter | Type | Required | Notes |
|---|---|---|---|
id | string, 24 hex characters | yes |
The envelope (below) with results of:
| Field | Type | Required | Notes |
|---|---|---|---|
id | string, 24 hex characters | no | |
name | string | no | |
code | string | null | no | Barcode / product code. |
hsnSac | string | null | no | |
taxPercent | number | no | One of 0, 5, 12, 18, 24, 28, 40. |
mrp | integer | no | Paise. |
salePrice | integer | no | Paise, GST-inclusive retail price. |
wholesalePrice | integer | null | no | Paise. |
unit | string | null | no | Primary unit code, e.g. "PCS", "KG". |
isStockTracked | boolean | no | |
stockLeft | number | null | no | Units left at the key's store; null when the product is not stock-tracked. |
status | string | no | ACTIVE or INACTIVE. |
batches | object[] | no | Only on GET /products/{id}, only when the store sells by batch: the batches with stock, oldest first. Name one on a sale line as batchId, or leave it out and the oldest is taken. |
id | string, 24 hex characters | no | |
batchNo | string | null | no | |
stockLeft | number | no | Units left in this batch. |
mrp | integer | null | no | Paise, this batch's printed MRP. |
salePrice | integer | null | no | Paise, this batch's price. |
expiryDate | string | null | no | YYYY-MM-DD. |
{
"status": true,
"statusCode": 200,
"correlationId": "2f6c1e0a9b3d4",
"results": {
"id": "66f1a2b3c4d5e6f7a8b9c0f3",
"name": "Aashirvaad Atta 5 kg",
"code": "8901058000123",
"hsnSac": "1101",
"taxPercent": 5,
"mrp": 31000,
"salePrice": 29500,
"wholesalePrice": 28000,
"unit": "PCS",
"isStockTracked": true,
"stockLeft": 28,
"status": "ACTIVE",
"batches": [
{
"id": "66f1a2b3c4d5e6f7a8b9c0b7",
"batchNo": "B-0912",
"stockLeft": 28,
"mrp": 31000,
"salePrice": 29500,
"expiryDate": "2027-03-31"
}
]
}
}
POST https://api.negobill.com/v1/salesCreate a sale. Send what was sold — the server prices catalogue lines from the product, computes GST per line, the totals, the rounding and the stock movement, and numbers the bill. Never send totals. A line with productId is a catalogue line (price from the product unless unitPrice is given, which must not be below the product's minimum sale price); a line with name and no productId is a free-text line and needs unitPrice and taxPercent. Sends no payment: a sale is unpaid until a payment is recorded (see POST /sales/{id}/payments). A sale with a contactId may stay unpaid; a sale without one must be paid with the next call. If a COMPLETED sale already carries the same reference it is returned with created:false and nothing is created. Counts in the plan's monthly sale quota like a sale made at the counter.
Scope: sales:write · Header: Authorization: Basic <base64(keyId:keySecret)> · Optional header: Idempotency-Key: <your unique key, ≤80 chars> · Content-Type: application/json
| Field | Type | Required | Notes |
|---|---|---|---|
reference | string | no | Your order id, 1–80 characters. Unique within the business; makes the call safe to retry. |
contactId | string, 24 hex characters | no | The customer (from POST /contacts). Required for a sale that will not be paid at once. |
docType | string | no | BILL or INVOICE. Omit to let the store's setting decide. INVOICE needs a billing profile with a GST number. |
saleDate | string | no | YYYY-MM-DD in the store timezone; today or a past day of this or the previous financial year. Default today. |
lines | object[] | yes | 1–500 lines. |
productId | string, 24 hex characters | no | A catalogue product. Either this or name. |
batchId | string, 24 hex characters | no | Catalogue lines in a batch-selling store: the batch to sell from (see GET /products/{id}). Left out, the oldest batch with stock is taken. |
name | string | no | Free-text line name, max 1000. Either this or productId. |
kind | string | no | Free-text lines only: PRODUCT or SERVICE, default SERVICE. |
quantity | number | yes | Up to 4 decimals. |
unitPrice | integer | no | Paise, GST-inclusive, per unit. Required for free-text lines; optional override for catalogue lines. 0 is allowed (free item). |
taxPercent | number | no | Free-text lines only: one of 0, 5, 12, 18, 24, 28, 40. Default 0. Catalogue lines take the product's. |
hsnSac | string | no | Free-text lines only, 4–8 digits. |
unit | string | no | Catalogue lines: a sale-unit tier name of the product (e.g. "BOX"); free-text lines: a label printed beside the quantity. |
note | string | no | Printed under the line, max 500 — the phone model a skin is for, an engraving text, a measurement. |
addOns | string[] | no | Catalogue lines only: keys of the product's add-on options to charge with this line (see GET /products/{id} → addOns). Each becomes a SERVICE line under the product at the option's catalogue price; an unknown key is refused with NB-B-SAL-1024; while the shop has add-on options switched off (Settings → Product) any key is refused with NB-B-SAL-1026. |
overallDiscount | integer | no | Paise taken off the whole bill, spread across the lines with GST recomputed per line. |
note | string | no | Max 300, printed on the bill. |
{
"reference": "order-5501",
"contactId": "66f1a2b3c4d5e6f7a8b9c0d1",
"lines": [
{
"productId": "66f1a2b3c4d5e6f7a8b9c0f3",
"quantity": 2
},
{
"name": "Delivery charge",
"kind": "SERVICE",
"quantity": 1,
"unitPrice": 5000,
"taxPercent": 18,
"hsnSac": "996812"
}
],
"note": "Web order 5501"
}
The envelope (below) with results of:
| Field | Type | Required | Notes |
|---|---|---|---|
id | string, 24 hex characters | no | The sale id. |
saleNo | string | no | The bill / invoice number as printed, e.g. "A20262027104". |
docType | string | no | BILL or INVOICE (Tax Invoice). |
status | string | no | COMPLETED or REVERSED (cancelled). |
saleDate | string | no | ISO 8601 UTC instant. |
saleDay | string | no | YYYY-MM-DD in the store timezone. |
reference | string | null | no | Your reference, if you gave one. |
contactId | string, 24 hex characters | null | no | |
customer | object | null | no | The customer as snapshotted on the sale. |
name | string | null | no | |
phone | string | null | no | |
gstNumber | string | null | no | |
lines | object[] | no | In the order NegoBill stores them (free-text lines before catalogue lines) — match by productId or name, not by position. |
productId | string, 24 hex characters | null | no | null for a free-text line. |
name | string | no | |
kind | string | no | PRODUCT or SERVICE. |
quantity | number | no | |
unit | string | null | no | |
unitPrice | integer | no | Paise, GST-inclusive. |
mrp | integer | null | no | Paise. |
taxPercent | number | no | |
taxAmount | integer | no | Paise, the GST inside this line. |
amount | integer | no | Paise, line total including GST. |
note | string | null | no | The line note as printed under the name (e.g. the phone model a skin was cut for). |
lineGroup | string | null | no | Set on a product line that carries add-on options and on each of its option lines — the same value groups them. |
addOnKey | string | null | no | On an add-on option line: the option key from the product (e.g. LOGO-CUT). null on ordinary lines. |
totals | object | no | All paise. |
taxable | integer | no | Sum of line amounts before GST. |
tax | integer | no | Total GST. |
total | integer | no | taxable + tax. |
roundOff | integer | no | Paise added (or removed, negative) to reach a whole rupee. |
grandTotal | integer | no | What the customer pays. Whole rupees, in paise. |
amountInWords | string | no | |
paid | integer | no | Paise received so far. |
due | integer | no | Paise still unpaid. 0 = settled. |
note | string | null | no | |
createdAt | string | no | ISO 8601 UTC. |
created | boolean | no | Only on POST: true when this call made the sale, false when a sale with the same reference already existed and was returned instead. |
{
"status": true,
"statusCode": 201,
"correlationId": "2f6c1e0a9b3d4",
"results": {
"id": "66f1a2b3c4d5e6f7a8b9c0e2",
"saleNo": "A20262027104",
"docType": "INVOICE",
"status": "COMPLETED",
"saleDate": "2026-09-18T09:31:12.000Z",
"saleDay": "2026-09-18",
"reference": "order-5501",
"contactId": "66f1a2b3c4d5e6f7a8b9c0d1",
"customer": {
"name": "Asha Stores",
"phone": "9876543210",
"gstNumber": "33ABCDE1234F1Z5"
},
"lines": [
{
"productId": "66f1a2b3c4d5e6f7a8b9c0f3",
"name": "Aashirvaad Atta 5 kg",
"kind": "PRODUCT",
"quantity": 2,
"unit": "PCS",
"unitPrice": 29500,
"mrp": 31000,
"taxPercent": 5,
"taxAmount": 2810,
"amount": 59000
},
{
"productId": null,
"name": "Delivery charge",
"kind": "SERVICE",
"quantity": 1,
"unit": null,
"unitPrice": 5000,
"mrp": null,
"taxPercent": 18,
"taxAmount": 763,
"amount": 5000
}
],
"totals": {
"taxable": 60427,
"tax": 3573,
"total": 64000,
"roundOff": 0,
"grandTotal": 64000,
"amountInWords": "Six Hundred Forty Rupees Only"
},
"paid": 0,
"due": 64000,
"note": "Web order 5501",
"createdAt": "2026-09-18T09:31:12.000Z",
"created": true
}
}
GET https://api.negobill.com/v1/salesLook up sales. By reference or saleNo (exact), by contactId, or by a day range; newest first, at most 50.
Scope: sales:read · Header: Authorization: Basic <base64(keyId:keySecret)>
| Parameter | Type | Required | Notes |
|---|---|---|---|
reference | string | no | |
saleNo | string | no | |
contactId | string, 24 hex characters | no | |
from | string | no | YYYY-MM-DD, inclusive. |
to | string | no | YYYY-MM-DD, inclusive. |
limit | integer | no | 1–50, default 20. |
The envelope (below) with results of:
| Field | Type | Required | Notes |
|---|---|---|---|
sales | object[] | no | |
id | string, 24 hex characters | no | The sale id. |
saleNo | string | no | The bill / invoice number as printed, e.g. "A20262027104". |
docType | string | no | BILL or INVOICE (Tax Invoice). |
status | string | no | COMPLETED or REVERSED (cancelled). |
saleDate | string | no | ISO 8601 UTC instant. |
saleDay | string | no | YYYY-MM-DD in the store timezone. |
reference | string | null | no | Your reference, if you gave one. |
contactId | string, 24 hex characters | null | no | |
customer | object | null | no | The customer as snapshotted on the sale. |
name | string | null | no | |
phone | string | null | no | |
gstNumber | string | null | no | |
lines | object[] | no | In the order NegoBill stores them (free-text lines before catalogue lines) — match by productId or name, not by position. |
productId | string, 24 hex characters | null | no | null for a free-text line. |
name | string | no | |
kind | string | no | PRODUCT or SERVICE. |
quantity | number | no | |
unit | string | null | no | |
unitPrice | integer | no | Paise, GST-inclusive. |
mrp | integer | null | no | Paise. |
taxPercent | number | no | |
taxAmount | integer | no | Paise, the GST inside this line. |
amount | integer | no | Paise, line total including GST. |
note | string | null | no | The line note as printed under the name (e.g. the phone model a skin was cut for). |
lineGroup | string | null | no | Set on a product line that carries add-on options and on each of its option lines — the same value groups them. |
addOnKey | string | null | no | On an add-on option line: the option key from the product (e.g. LOGO-CUT). null on ordinary lines. |
totals | object | no | All paise. |
taxable | integer | no | Sum of line amounts before GST. |
tax | integer | no | Total GST. |
total | integer | no | taxable + tax. |
roundOff | integer | no | Paise added (or removed, negative) to reach a whole rupee. |
grandTotal | integer | no | What the customer pays. Whole rupees, in paise. |
amountInWords | string | no | |
paid | integer | no | Paise received so far. |
due | integer | no | Paise still unpaid. 0 = settled. |
note | string | null | no | |
createdAt | string | no | ISO 8601 UTC. |
created | boolean | no | Only on POST: true when this call made the sale, false when a sale with the same reference already existed and was returned instead. |
{
"status": true,
"statusCode": 200,
"correlationId": "2f6c1e0a9b3d4",
"results": {
"sales": [
{
"id": "66f1a2b3c4d5e6f7a8b9c0e2",
"saleNo": "A20262027104",
"docType": "INVOICE",
"status": "COMPLETED",
"saleDate": "2026-09-18T09:31:12.000Z",
"saleDay": "2026-09-18",
"reference": "order-5501",
"contactId": "66f1a2b3c4d5e6f7a8b9c0d1",
"customer": {
"name": "Asha Stores",
"phone": "9876543210",
"gstNumber": "33ABCDE1234F1Z5"
},
"lines": [
{
"productId": "66f1a2b3c4d5e6f7a8b9c0f3",
"name": "Aashirvaad Atta 5 kg",
"kind": "PRODUCT",
"quantity": 2,
"unit": "PCS",
"unitPrice": 29500,
"mrp": 31000,
"taxPercent": 5,
"taxAmount": 2810,
"amount": 59000
},
{
"productId": null,
"name": "Delivery charge",
"kind": "SERVICE",
"quantity": 1,
"unit": null,
"unitPrice": 5000,
"mrp": null,
"taxPercent": 18,
"taxAmount": 763,
"amount": 5000
}
],
"totals": {
"taxable": 60427,
"tax": 3573,
"total": 64000,
"roundOff": 0,
"grandTotal": 64000,
"amountInWords": "Six Hundred Forty Rupees Only"
},
"paid": 0,
"due": 64000,
"note": "Web order 5501",
"createdAt": "2026-09-18T09:31:12.000Z"
}
]
}
}
GET https://api.negobill.com/v1/sales/{id}Read a sale. Including what is paid and what is still due.
Scope: sales:read · Header: Authorization: Basic <base64(keyId:keySecret)>
| Parameter | Type | Required | Notes |
|---|---|---|---|
id | string, 24 hex characters | yes |
The envelope (below) with results of:
| Field | Type | Required | Notes |
|---|---|---|---|
id | string, 24 hex characters | no | The sale id. |
saleNo | string | no | The bill / invoice number as printed, e.g. "A20262027104". |
docType | string | no | BILL or INVOICE (Tax Invoice). |
status | string | no | COMPLETED or REVERSED (cancelled). |
saleDate | string | no | ISO 8601 UTC instant. |
saleDay | string | no | YYYY-MM-DD in the store timezone. |
reference | string | null | no | Your reference, if you gave one. |
contactId | string, 24 hex characters | null | no | |
customer | object | null | no | The customer as snapshotted on the sale. |
name | string | null | no | |
phone | string | null | no | |
gstNumber | string | null | no | |
lines | object[] | no | In the order NegoBill stores them (free-text lines before catalogue lines) — match by productId or name, not by position. |
productId | string, 24 hex characters | null | no | null for a free-text line. |
name | string | no | |
kind | string | no | PRODUCT or SERVICE. |
quantity | number | no | |
unit | string | null | no | |
unitPrice | integer | no | Paise, GST-inclusive. |
mrp | integer | null | no | Paise. |
taxPercent | number | no | |
taxAmount | integer | no | Paise, the GST inside this line. |
amount | integer | no | Paise, line total including GST. |
note | string | null | no | The line note as printed under the name (e.g. the phone model a skin was cut for). |
lineGroup | string | null | no | Set on a product line that carries add-on options and on each of its option lines — the same value groups them. |
addOnKey | string | null | no | On an add-on option line: the option key from the product (e.g. LOGO-CUT). null on ordinary lines. |
totals | object | no | All paise. |
taxable | integer | no | Sum of line amounts before GST. |
tax | integer | no | Total GST. |
total | integer | no | taxable + tax. |
roundOff | integer | no | Paise added (or removed, negative) to reach a whole rupee. |
grandTotal | integer | no | What the customer pays. Whole rupees, in paise. |
amountInWords | string | no | |
paid | integer | no | Paise received so far. |
due | integer | no | Paise still unpaid. 0 = settled. |
note | string | null | no | |
createdAt | string | no | ISO 8601 UTC. |
created | boolean | no | Only on POST: true when this call made the sale, false when a sale with the same reference already existed and was returned instead. |
{
"status": true,
"statusCode": 200,
"correlationId": "2f6c1e0a9b3d4",
"results": {
"id": "66f1a2b3c4d5e6f7a8b9c0e2",
"saleNo": "A20262027104",
"docType": "INVOICE",
"status": "COMPLETED",
"saleDate": "2026-09-18T09:31:12.000Z",
"saleDay": "2026-09-18",
"reference": "order-5501",
"contactId": "66f1a2b3c4d5e6f7a8b9c0d1",
"customer": {
"name": "Asha Stores",
"phone": "9876543210",
"gstNumber": "33ABCDE1234F1Z5"
},
"lines": [
{
"productId": "66f1a2b3c4d5e6f7a8b9c0f3",
"name": "Aashirvaad Atta 5 kg",
"kind": "PRODUCT",
"quantity": 2,
"unit": "PCS",
"unitPrice": 29500,
"mrp": 31000,
"taxPercent": 5,
"taxAmount": 2810,
"amount": 59000
},
{
"productId": null,
"name": "Delivery charge",
"kind": "SERVICE",
"quantity": 1,
"unit": null,
"unitPrice": 5000,
"mrp": null,
"taxPercent": 18,
"taxAmount": 763,
"amount": 5000
}
],
"totals": {
"taxable": 60427,
"tax": 3573,
"total": 64000,
"roundOff": 0,
"grandTotal": 64000,
"amountInWords": "Six Hundred Forty Rupees Only"
},
"paid": 0,
"due": 64000,
"note": "Web order 5501",
"createdAt": "2026-09-18T09:31:12.000Z"
}
}
GET https://api.negobill.com/v1/sales/{id}/paperThe bill as HTML. The printable A4 bill, rendered by NegoBill from the sale exactly as the shop prints it (text/html, not JSON).
Scope: sales:read · Header: Authorization: Basic <base64(keyId:keySecret)>
| Parameter | Type | Required | Notes |
|---|---|---|---|
id | string, 24 hex characters | yes |
text/html — a complete HTML document.
<!doctype html><html>…</html>
POST https://api.negobill.com/v1/sales/{id}/paymentsRecord a payment. Records money received against this sale under one of the business's payment methods (by name, case-insensitive — the error lists the names when yours is unknown). The amount must not exceed what is due. If a receipt already carries the same reference it is returned with created:false.
Scope: payments:write · Header: Authorization: Basic <base64(keyId:keySecret)> · Optional header: Idempotency-Key: <your unique key, ≤80 chars> · Content-Type: application/json
| Parameter | Type | Required | Notes |
|---|---|---|---|
id | string, 24 hex characters | yes | The sale. |
| Field | Type | Required | Notes |
|---|---|---|---|
reference | string | no | Your payment id, 1–80 characters. Unique within the business. |
amount | integer | yes | Paise, at most the sale's due. |
method | string | yes | A payment method name of the business, e.g. "UPI", "Cash", "Bank transfer", "Card". |
note | string | no | Max 300. |
{
"reference": "pay-5501",
"amount": 64000,
"method": "UPI"
}
The envelope (below) with results of:
| Field | Type | Required | Notes |
|---|---|---|---|
id | string, 24 hex characters | no | The receipt id. |
receiptNo | string | no | |
amount | integer | no | Paise. |
method | string | null | no | The payment method name. |
saleId | string, 24 hex characters | null | no | |
reference | string | null | no | |
receiptDate | string | no | ISO 8601 UTC. |
due | integer | no | Paise still unpaid on the sale after this receipt. |
created | boolean | no | Only on POST: false when a receipt with the same reference already existed and was returned instead. |
{
"status": true,
"statusCode": 201,
"correlationId": "2f6c1e0a9b3d4",
"results": {
"id": "66f1a2b3c4d5e6f7a8b9c1a4",
"receiptNo": "R20262027211",
"amount": 64000,
"method": "UPI",
"saleId": "66f1a2b3c4d5e6f7a8b9c0e2",
"reference": "pay-5501",
"receiptDate": "2026-09-18T09:32:40.000Z",
"due": 0,
"created": true
}
}
GET https://api.negobill.com/v1/sales/{id}/paymentsPayments on a sale.
Scope: payments:read · Header: Authorization: Basic <base64(keyId:keySecret)>
| Parameter | Type | Required | Notes |
|---|---|---|---|
id | string, 24 hex characters | yes |
The envelope (below) with results of:
| Field | Type | Required | Notes |
|---|---|---|---|
payments | object[] | no | |
id | string, 24 hex characters | no | The receipt id. |
receiptNo | string | no | |
amount | integer | no | Paise. |
method | string | null | no | The payment method name. |
saleId | string, 24 hex characters | null | no | |
reference | string | null | no | |
receiptDate | string | no | ISO 8601 UTC. |
due | integer | no | Paise still unpaid on the sale after this receipt. |
created | boolean | no | Only on POST: false when a receipt with the same reference already existed and was returned instead. |
due | integer | no | Paise still unpaid. |
{
"status": true,
"statusCode": 200,
"correlationId": "2f6c1e0a9b3d4",
"results": {
"payments": [
{
"id": "66f1a2b3c4d5e6f7a8b9c1a4",
"receiptNo": "R20262027211",
"amount": 64000,
"method": "UPI",
"saleId": "66f1a2b3c4d5e6f7a8b9c0e2",
"reference": "pay-5501",
"receiptDate": "2026-09-18T09:32:40.000Z",
"due": 0
}
],
"due": 0
}
}
| HTTP | code | Meaning | What to do |
|---|---|---|---|
| 400 | NB-B-API-1000 | External API request validation failed | Fix the request (details lists each field) and retry with a NEW Idempotency-Key. |
| 401 | NB-B-API-1001 | No credentials — Authorization: Basic <base64(keyId:keySecret)> missing or malformed | Send the Authorization header. Do not retry unchanged. |
| 401 | NB-B-API-1002 | Key ID unknown, secret wrong, or the key was revoked | Check the Key ID and secret in the console; a revoked key never works again. Alert a human. |
| 403 | NB-B-API-1003 | The API Access add-on is not active on this business (plan lacks it, or the module is switched off) | The business must add the API Access add-on in Settings → Billing & Plans (Plus and above), or switch the API module on. Alert a human. |
| 403 | NB-B-API-1004 | The key lacks the scope this call needs | Ask the owner to add the scope to the key in Settings → API Access. Do not retry unchanged. |
| 429 | NB-B-API-1005 | Rate limit reached for this key | Wait for Retry-After seconds, then retry the same request. |
| 409 | NB-B-API-1006 | Idempotency-Key reused with a different body, or the first request is still running | A key reused with a different body: use a new key. "Still being processed": wait a second and retry the same request. |
| 404 | NB-B-API-1007 | Not found in this business | The id is not in this business. Do not retry. |
| 403 | NB-B-API-1008 | The calling IP is not on the key allowlist | Add the calling server's IP to the key's allowlist in the console. Alert a human. |
| 400 | NB-B-SAL-1000 | Sale validation failed (a line, a price below the product's minimum, a bad date) | Fix the request; details names the field. |
| 404 | NB-B-SAL-1002 | Store or billing profile not found | Alert a human: the key's store is not set up. |
| 400 | NB-B-SAL-1012 | INVOICE asked for but the billing profile has no GST number | Send docType BILL, or omit it. |
| 403 | NB-B-PLN-1006 | The plan's monthly sale quota is reached | Stop creating sales; alert a human — the business must upgrade its plan. |
| 400 | NB-B-PAY-1000 | Payment validation failed (amount above the due, unknown method) | Fix the request; details says what. |
| 400 | NB-B-CON-1000 | Contact validation failed | Fix the request. |
| 409 | NB-B-CON-1001 | A contact with this phone already exists | Look the contact up (GET /contacts?phone=) and use its id. |
| 500 | NB-B-CORE-1000 | An internal error | Retry with backoff (same Idempotency-Key); after three failures alert a human and quote correlationId. |
Rule of thumb: 400/404/409 — fix the request, do not retry unchanged; 401/403 — a credential, scope, plan or allowlist matter for a human; 429 — wait Retry-After and retry; 5xx — retry with backoff and the same Idempotency-Key.
This is v1. Fields are only ever added; a removed or renamed field, a changed type or a changed status code would ship as /v2 with this page kept for v1 and a deprecation date announced here.