OneTrails developers
Public API 1.0.0 · read data and create drafts or proposals. Nothing is posted, filed, sent or approved through the API.
API reference
books
POST/v1/entities/{eid}/journalsCreate a manual journal draft (journal, contra, opening) (creates a draft)GET/v1/entities/{eid}/reports/ledgerGeneral ledger (account or group): opening, lines with running balance, closingGET/v1/entities/{eid}/reports/trial-balanceTrial balance with group tree and the balance checkGET/v1/entities/{eid}/vouchersJournal register feed: filters, sort, cursorGET/v1/entities/{eid}/vouchers/{id}A voucher with its lines and source link
clients
GET/v1/client-accountsClient accounts with entity counts (scope-filtered)GET/v1/entitiesEntities (filters: account, type, status, GSTIN, PAN, assignee, profile completeness)
me
GET/v1/meBootstrap for the first screen (BR-M02-20)
purchases
GET/v1/entities/{eid}/billsBills and supplier notes: status, kind, ITC status, party and date filters, q, cursor; format=csv|xlsx exports the whole filtered list (books.reports.export; audited as export)POST/v1/entities/{eid}/billsCreate a bill / supplier note draft (tax, ITC category, RCM, TDS suggestion; duplicate warning) (creates a draft)GET/v1/entities/{eid}/bills/{id}A bill with its lines and the Rule 37 tracker projection
sales
GET/v1/entities/{eid}/invoicesInvoices and notes: status, payment, e-invoice, party, date and q filters, cursor; format=csv|xlsx exports the whole filtered list (books.reports.export; audited as export)POST/v1/entities/{eid}/invoicesCreate an invoice / credit note / debit note draft (rates looked up; RATE_AMBIGUOUS needs an override with a reason) (creates a draft)GET/v1/entities/{eid}/invoices/{id}A document with its linesGET/v1/entities/{eid}/partiesParties: search q (name / GSTIN / PAN), roles and status filters, cursorPOST/v1/entities/{eid}/partiesCreate a party with its GSTINs (BR-M15-01: checksum, state, PAN match, one default) (creates a draft)GET/v1/entities/{eid}/parties/{id}A party with its GSTINsPATCH/v1/entities/{eid}/parties/{id}Edit a party (If-Match); `gstins` replaces the list (creates a draft)
work
GET/v1/work/jobsWork board: jobs with stage counts (scope-filtered)GET/v1/work/jobs/{id}Job with tasks, client actions, reviews, SLA and history
Verify webhook signatures
Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(secret, header, rawBody, now = Math.floor(Date.now() / 1000)) {
const parts = header.split(',');
const t = Number(parts.find((p) => p.startsWith('t='))?.slice(2));
if (Math.abs(now - t) > 300) return false;
const want = createHmac('sha256', secret).update(t + '.' + rawBody).digest();
return parts.filter((p) => p.startsWith('v1=')).some((p) => {
const got = Buffer.from(p.slice(3), 'hex');
return got.length === want.length && timingSafeEqual(got, want);
});
}Python
import hashlib, hmac, time
def verify(secret: str, header: str, raw_body: bytes) -> bool:
parts = [p.strip() for p in header.split(",")]
t = int(next(p[2:] for p in parts if p.startswith("t=")))
if abs(int(time.time()) - t) > 300:
return False
want = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return any(hmac.compare_digest(p[3:], want) for p in parts if p.startswith("v1="))Try it
Uses a sandbox key held only in this page's memory.