Integrations
API documentation
Pull scanned invoice, purchase order and bank-entry JSON straight into Tally, Zoho Books, Zoho Inventory, SAP Business One, SAP S/4HANA, Busy, Marg, QuickBooks, Xero or any custom in-house software using two simple REST endpoints. Sign in to generate an API key — the key in the examples below is masked for safety.
Uniform JSON via Replacement Rules
Every scan is normalised through your org's Rename JSON Variables rules before delivery. Rules cover two levels:
- Variable names: map many raw labels (e.g. "Supplier", "Party Name", "Vendor Name") to one canonical key like
vendor_name. - Values: map many raw values to one clean value — e.g.
state: "MH", "M.H.", "Maharastra" →"Maharashtra". - Vendor and Product mapping tables auto-normalise
vendor_nameandproduct_namein the response.
The result: every downstream system (Tally, Zoho, SAP, custom ERP) receives the same keys and value spellings, so no per-invoice mapping logic is needed on your side.
Authentication
Every request must include an API key. Sign in and visit the API keys page to create one. Send it as a Bearer token in the Authorization header. Keys are scoped to a single user and are shown in full only once at creation time — store yours securely.
Authorization: Bearer sp_live_••••••••••••••••••••••••••••••Alternatively send x-api-key: sp_live_•••••••••••••••••••••••••••••• if Bearer headers are not supported by your client.
Setup in Tally (one-time)
The end-user flow is just two steps: Upload Document and Sync Tally to upload invoice. To enable the sync side, do this one-time setup:
- Copy your API key — sign in, open API keys and copy your default key (or create a new one).
- Install our Tally TDL — ping us on WhatsApp to receive the TDL file, then load it inside Tally (Gateway → F1: Manage Local TDL → point to the .tdl file).
- Paste the API key into the TDL prompt — open the TDL menu inside Tally and paste your key when prompted. Tally will remember it.
- Hit Sync — the TDL calls
GET /scans/unsynced, posts each invoice as a bill, then callsPOST /scans/mark-syncedso the same invoice never imports twice.
After setup, your users only ever do two things: drop an invoice in the chat box (or upload it) and click Sync inside Tally.
GET /api/public/v1/scans/unsynced
Returns every successful scan that has not yet been marked as synced. Duplicates by invoice_no are removed (the oldest record wins); scans without an invoice number are all returned.
Query parameters
limit— optional, default 500, max 1000.category— optional, one ofvendor_invoice,purchase_order, orbank_entry. Omit to return all categories.
Request
curl 'https://scan.innosate.com/api/public/v1/scans/unsynced?category=vendor_invoice&limit=200' \
-H 'Authorization: Bearer sp_live_••••••••••••••••••••••••••••••'Response 200
{
"count": 2,
"items": [
{
"id": "5d2c7b8a-1c1a-4a25-9b6d-1c43f2c1aa01",
"invoice_no": "INV-1042",
"filename": "1042.pdf",
"category": "vendor_invoice",
"created_at": "2026-06-15T09:11:43.221Z",
"data": {
"invoice_metadata": { "invoice_no": "INV-1042", "invoice_date": "2026-06-15", "gstin": "27ABCDE1234F1Z5", "name": "Acme Traders", "...": "..." },
"line_items": [
{ "description": "Widget A", "hsn": "8471", "qty": 10, "rate": 250, "amount": 2500 }
]
}
},
{
"id": "f2e8c1d2-aa11-4d6c-93b7-2f8e21a90b22",
"invoice_no": "INV-1043",
"filename": "1043.pdf",
"created_at": "2026-06-15T09:18:02.001Z",
"data": { "invoice_metadata": { "invoice_no": "INV-1043" }, "line_items": [] }
}
]
}POST /api/public/v1/scans/mark-synced
Mark scans as synced so they will not be returned by the unsynced endpoint again. Provide either ids, invoice_nos, or both. Already-synced rows are ignored.
Body (JSON)
{
"ids": ["5d2c7b8a-1c1a-4a25-9b6d-1c43f2c1aa01"],
"invoice_nos": ["INV-1043"],
"source": "tally-tdl"
}Request
curl -X POST 'https://scan.innosate.com/api/public/v1/scans/mark-synced' \
-H 'Authorization: Bearer sp_live_••••••••••••••••••••••••••••••' \
-H 'Content-Type: application/json' \
-d '{"invoice_nos":["INV-1042","INV-1043"],"source":"tally-tdl"}'Response 200
{ "ok": true, "updated": 2, "synced_at": "2026-06-17T11:24:08.512Z" }Vendors API
Manage the canonical vendor list and aliases used to match vendor names on scanned bank_entry rows. Only an API key owned by the reseller (org owner) can create or update vendors; any authenticated key can list.
GET /api/public/v1/vendors
curl 'https://scan.innosate.com/api/public/v1/vendors' \
-H 'Authorization: Bearer sp_live_••••••••••••••••••••••••••••••'{
"count": 1,
"items": [
{
"id": "a1c...",
"name": "Acme Traders Pvt Ltd",
"updated_at": "2026-06-15T09:11:43.221Z",
"aliases": [{ "id": "…", "alias": "ACME TRADERS" }, { "id": "…", "alias": "acme-traders" }]
}
]
}POST /api/public/v1/vendors
Creates the vendor if the name is new, or reuses the existing vendor with that name. Provided aliases are appended (duplicates collapsed by normalized value).
{
"name": "Acme Traders Pvt Ltd",
"aliases": ["ACME TRADERS", "acme-traders", "Acme Trdrs"]
}curl -X POST 'https://scan.innosate.com/api/public/v1/vendors' \
-H 'Authorization: Bearer sp_live_••••••••••••••••••••••••••••••' \
-H 'Content-Type: application/json' \
-d '{"name":"Acme Traders Pvt Ltd","aliases":["ACME TRADERS","acme-traders"]}'{ "ok": true, "id": "a1c...", "created": true, "aliases_added": 2 }Products API
Manage the canonical product list and aliases used to normalise product names on invoice line items. Only a reseller (org owner) key can create or update; any authenticated key can list.
GET /api/public/v1/products
curl 'https://scan.innosate.com/api/public/v1/products' \
-H 'Authorization: Bearer sp_live_••••••••••••••••••••••••••••••'{
"count": 1,
"items": [
{
"id": "p1c...",
"name": "Widget A",
"updated_at": "2026-06-15T09:11:43.221Z",
"aliases": [{ "id": "…", "alias": "WIDGET-A" }, { "id": "…", "alias": "widget a 10mm" }]
}
]
}POST /api/public/v1/products
{
"name": "Widget A",
"aliases": ["WIDGET-A", "widget a 10mm", "wdgt-a"]
}curl -X POST 'https://scan.innosate.com/api/public/v1/products' \
-H 'Authorization: Bearer sp_live_••••••••••••••••••••••••••••••' \
-H 'Content-Type: application/json' \
-d '{"name":"Widget A","aliases":["WIDGET-A","widget a 10mm"]}'{ "ok": true, "id": "p1c...", "created": false, "aliases_added": 2 }Errors
401— missing, invalid, or revoked API key.400— invalid JSON body, or neitheridsnorinvoice_nosprovided.500— unexpected server error; safe to retry.
{ "error": "Invalid API key." }Recommended sync flow
- Call
GET /scans/unsyncedto fetch pending records (filter bycategoryif needed). - Post each
items[].dataJSON into your target system (Tally, Zoho, SAP, custom ERP…). - Collect the successfully imported
ids (orinvoice_nos). - Call
POST /scans/mark-syncedwith that list so the same records are not delivered again. - Repeat on a schedule (e.g. every 5 minutes via cron, Windows Task Scheduler, n8n, Make, Zapier, or your own worker).
Integrating with Zoho, SAP & custom software
The API is plain REST + JSON so it plugs into any accounting or ERP system that can make an HTTP call. Below are ready-to-adapt snippets. In every case the pattern is the same: GET /scans/unsynced → push into the target system → POST /scans/mark-synced.
Tally (TDL / Prime)
Use our shipped TDL (see setup above) or write your own HTTP Post using Tally's HTTP Post Requests action. The JSON matches Tally's Voucher / Purchase Bill XML mapping fields (party, invoice_no, date, ledger, GST, line items).
Zoho Books / Zoho Inventory (Node.js)
// 1. Pull vendor invoices from ScanParse
const res = await fetch('https://scan.innosate.com/api/public/v1/scans/unsynced?category=vendor_invoice', {
headers: { Authorization: 'Bearer sp_live_••••••••••••••••••••••••••••••' },
});
const { items } = await res.json();
// 2. Push each one to Zoho Books as a Bill
for (const s of items) {
const md = s.data.invoice_metadata ?? {};
await fetch(`https://www.zohoapis.in/books/v3/bills?organization_id=${ORG_ID}`, {
method: 'POST',
headers: {
Authorization: `Zoho-oauthtoken ${ZOHO_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
vendor_name: md.name,
bill_number: md.invoice_no,
date: md.invoice_date,
line_items: (s.data.line_items ?? []).map((li) => ({
name: li.product_name ?? li.description,
quantity: li.qty,
rate: li.rate,
})),
}),
});
}
// 3. Mark them synced so we don't re-import
await fetch('https://scan.innosate.com/api/public/v1/scans/mark-synced', {
method: 'POST',
headers: {
Authorization: 'Bearer sp_live_••••••••••••••••••••••••••••••',
'Content-Type': 'application/json',
},
body: JSON.stringify({ ids: items.map((i) => i.id), source: 'zoho-books' }),
});SAP Business One / S/4HANA (Service Layer)
# 1. Fetch purchase orders from ScanParse
curl 'https://scan.innosate.com/api/public/v1/scans/unsynced?category=purchase_order' \
-H 'Authorization: Bearer sp_live_••••••••••••••••••••••••••••••' -o pending.json
# 2. For each item, POST to SAP B1 Service Layer PurchaseOrders endpoint
# (or use OData v4 API_PURCHASEORDER_PROCESS_SRV on S/4HANA)
curl -X POST 'https://sap-b1:50000/b1s/v1/PurchaseOrders' \
-H "Cookie: B1SESSION=$SESSION" \
-H 'Content-Type: application/json' \
-d @po.json
# 3. Mark synced
curl -X POST 'https://scan.innosate.com/api/public/v1/scans/mark-synced' \
-H 'Authorization: Bearer sp_live_••••••••••••••••••••••••••••••' \
-H 'Content-Type: application/json' \
-d '{"ids":["<uuid>"],"source":"sap-b1"}'Custom software (Python)
import requests
API = "https://scan.innosate.com/api/public/v1"
KEY = "sp_live_••••••••••••••••••••••••••••••"
H = {"Authorization": f"Bearer {KEY}"}
# 1. Pull any category: vendor_invoice | purchase_order | bank_entry
pending = requests.get(f"{API}/scans/unsynced", params={"category": "bank_entry"},
headers=H).json()
for s in pending["items"]:
# 2. Push into your ERP / DB / accounting engine
my_erp.post_bank_entry(s["data"]) # your integration code
# 3. Confirm
requests.post(f"{API}/scans/mark-synced",
headers={**H, "Content-Type": "application/json"},
json={"ids": [i["id"] for i in pending["items"]],
"source": "custom-erp"})No-code (Zapier / Make / n8n / Power Automate)
- Trigger: Schedule every 5 minutes.
- Step 1: HTTP GET
https://scan.innosate.com/api/public/v1/scans/unsynced?category=vendor_invoicewith theAuthorizationheader. - Step 2: Iterate
items[]and mapdata.invoice_metadata+data.line_itemsto your target app's "Create Bill" / "Create Invoice" action. - Step 3: HTTP POST
https://scan.innosate.com/api/public/v1/scans/mark-syncedwith the importedids.
Need a ready-made connector for a system not listed here? Ping us on WhatsApp — we ship new integrations continuously.
Data retention (30 days)
Uploaded documents and the JSON we generate from them are stored for 30 days, then permanently deleted by a daily cleanup job. This applies to both synced and unsynced scans.
GET /scans/unsyncedonly ever returns scans from the last 30 days — older records won't reappear.- Sync to Tally (or download the JSON) before the 30-day window closes if you need to keep a copy.
- Credits and account history are not affected — only the document and its JSON payload are removed.