Clients & documents
Client CRUD with dedupe, plus invoices, quotations, proforma, debit notes, and credit notes. PDF download, share links, convert-to-invoice.
Interactive reference
Copy-ready curl commands target the production base. Send request uses the Try-it base shown in the console (local MAMP when developing locally).
Stored in
localStorage
only · sent as
Authorization: Bearer …
and
X-API-Key
(Try-it requires both on Apache)
Try-it and curl both use this production base URL.
Overview
Versioned JSON REST for clients, all document types (invoices, quotations, proforma, debit/credit notes), inventory, expenses, sendouts, mobile notifications, analytics, TDS, settings, and Shopify — the same data as your dashboard.
Client CRUD with dedupe, plus invoices, quotations, proforma, debit notes, and credit notes. PDF download, share links, convert-to-invoice.
Keep stock and costs aligned with your books and PDFs.
Email document PDFs, sendout history, FY-scoped analytics, TDS reports, and account settings via API.
Per-account keys with the same subscription rules as the web app.
Send your key as
Authorization: Bearer <api_key>
or
X-API-Key: <api_key>.
Create or rotate it under
Dashboard → API access
.
?api_key=
is for quick tests only, not production.
Success:
{"ok": true, "data": …}
Error:
{"ok": false, "error": {"code", "message", "details"}}
curl -sS -H "Authorization: Bearer YOUR_API_KEY" "https://api.onestopinvoice.com/v1/me"
Endpoints at a glance
| Methods | Path | Notes |
|---|---|---|
| GET |
/v1/me
|
Account summary |
| GETPOST |
/v1/clients
|
List & create · dedupe on phone / email / GSTIN · POST bulk-delete |
| GETPATCHDELETE |
/v1/clients/{id}
|
Read, update, delete (?cascade=1) |
| GETPOST |
/v1/invoices
|
FY/search/status filters · POST bulk-delete |
| GETPATCHDELETE |
/v1/invoices/{id}
|
Optional TDS fields on create/update |
| POST |
/v1/invoices/bulk-pdf
|
ZIP of invoice PDFs (binary) |
| GET |
/v1/invoices/recent-payments
|
Paid invoices since ?since= timestamp |
| GETPOST |
/v1/quotations
|
List filters · POST bulk-delete · convert-to-invoice |
| GETPATCHDELETE |
/v1/quotations/{id}
|
|
| GETPOST |
/v1/debit-notes
|
List filters · POST bulk-delete |
| GETPATCHDELETE |
/v1/debit-notes/{id}
|
|
| GETPOST |
/v1/credit-notes
|
List filters · source_invoice_id on create |
| GETPATCHDELETE |
/v1/credit-notes/{id}
|
POST bulk-delete on collection |
| GETPOST |
/v1/proforma-invoices
|
Dedicated proforma table · convert-to-invoice |
| GETPATCHDELETE |
/v1/proforma-invoices/{id}
|
|
| GET |
/v1/documents/{type}/{id}/pdf
|
Download PDF (binary) |
| GETPOSTDELETE |
/v1/documents/{type}/{id}/share
|
Public share links |
| GETPOST |
/v1/inventory
|
POST bulk-delete |
| GETPATCHDELETE |
/v1/inventory/{id}
|
|
| GET |
/v1/expenses/categories
|
Static expense category list |
| GET |
/v1/expenses/{id}/repeat
|
Prefill for repeat expense entry |
| GETPOST |
/v1/expenses
|
POST bulk-delete |
| GETPATCHDELETE |
/v1/expenses/{id}
|
|
| GETPOSTDELETE |
/v1/sendouts
|
Email PDF · history · templates |
| POST |
/v1/auth/login
|
Mobile sign-in → access_token |
| POST |
/v1/auth/refresh
|
Rotate mobile access token |
| GET |
/v1/auth/me
|
Current mobile user |
| GETPATCHPOST |
/v1/notifications
|
In-app feed · unread count · mark read · prefs |
| POSTDELETE |
/v1/devices
|
Register / unregister FCM token |
| GET |
/v1/analytics/summary
|
Dashboard aggregates · ?fy_start_year= |
| GET |
/v1/analytics/full
|
Extended insights (expenses, TDS, inventory, GST) |
| GETPOSTPATCHDELETE |
/v1/tds/sections
|
TDS section CRUD |
| GETPATCH |
/v1/tds/deductions
|
Deductions · deposit / undo-deposit |
| POST |
/v1/tds/certificates
|
Issue Form 16A |
| GET |
/v1/tds/reports
|
FY TDS reports |
| POST |
/v1/tds/calculate
|
TDS amount calculator |
| GETPATCH |
/v1/settings/business
|
Business profile |
| GETPOSTPATCHDELETE |
/v1/settings/users
|
Sub-user management |
| GETPATCH |
/v1/settings/templates
|
Document template prefs |
| GETPATCHPOST |
/v1/settings/ca-sync
|
CA sync · test-send |
| GET |
/v1/settings/api-key
|
Masked API key (read-only) |
| GETPOSTDELETE |
/v1/integrations/shopify
|
Connect · backfill · webhooks · logs |
Developer guide
Authentication, envelopes, pagination, dedupe rules, and integration recipes.
JSON API under /v1/… on the same host as the web app (or on a subdomain whose document root is this project). Implementation entrypoint: `api/v1/index.php`.
The API hub and reference show the public base `https://api.onestopinvoice.com/v1` even on localhost, so copy-paste targets production. To override (e.g. staging), set `ONESTOP_API_V1_BASE`.
api permission) and copy your key. - Header Authorization: Bearer <your_api_key>
- Header X-API-Key: <your_api_key>
- Query ?api_key= (discouraged; logs and referrer leakage)
The key is stored on accounts.api_key. Invalid or missing key → 401 with error.code = unauthorized.
Same rules as the logged-in app:
403 (account_suspended)403 (subscription_expired)Success:
{ "ok": true, "data": { } }
Error:
{
"ok": false,
"error": {
"code": "validation_error",
"message": "…",
"details": { }
}
}
If Origin is sent, the API reflects it and allows GET, POST, PATCH, DELETE, OPTIONS with Authorization, Content-Type, and X-API-Key. Preflight OPTIONS returns 204.
List endpoints accept limit (default 50, max 200) and offset (default 0).
Invoice, quotation, debit note, credit note, and proforma invoice lists accept the same filter query params (combine as needed):
| Param | Applies to | Values / format |
|-------|------------|-----------------|
| fy_start_year | All document lists | Integer FY start year (Apr–Mar), e.g. 2025 for FY 2025–26 |
| date_from, date_to | All | Y-m-d document date range (ignored when fy_start_year is set) |
| client_id | All | Positive integer |
| search | All | Matches invoice/quotation number or client name |
| status | Invoices, debit/credit notes | draft, issued, cancelled, archived |
| payment_status | Invoices | pending, paid, partial, overdue |
| due_from, due_to | Invoices | Due date range (Y-m-d) |
| payment_date_from, payment_date_to | Invoices | Payment date range |
| approval_status | Quotations, proforma | pending, approved, rejected |
| document_kind | Quotations only | quotation or proforma (legacy rows on quotations table) |
Example — invoices for FY 2025–26, paid only:
curl -sS -H "Authorization: Bearer YOUR_API_KEY" \
"https://api.onestopinvoice.com/v1/invoices?fy_start_year=2025&payment_status=paid&limit=50"
POST /v1/clients checks existing rows in your account. If any of the following match a non-empty value you send, the API returns 200 with data.skipped: true and the existing data.client (no new row):
DELETE /v1/clients/{id} without related documents succeeds directly. If related invoices, quotations, proforma invoices, debit notes, or credit notes exist, you get 409 unless you call DELETE /v1/clients/{id}?cascade=1, which removes related document rows, nulls expense.client_id for that client, then deletes the client.
Bulk delete — POST /v1/{resource}/bulk-delete with JSON body {"ids": [1, 2, 3]}. Supported resources: clients, invoices, quotations, debit-notes, credit-notes, proforma-invoices, inventory, expenses. Returns counts of deleted and skipped ids.
Bulk invoice PDF — POST /v1/invoices/bulk-pdf with {"ids": [101, 102]}. Returns a ZIP file of PDFs (binary response, not JSON).
Recent payments — GET /v1/invoices/recent-payments?since=2026-03-01%2000:00:00 returns invoices marked paid since the timestamp (useful for integrations polling payment status).
Some endpoints return files instead of the JSON envelope:
| Endpoint | Content-Type |
|----------|----------------|
| GET /v1/documents/{type}/{id}/pdf | application/pdf |
| POST /v1/invoices/bulk-pdf | application/zip |
Send Accept: application/json on JSON endpoints. For PDF/ZIP, save the raw response body.
Same document root as the main site (recommended): `.htaccess` maps /v1/... to api/v1/index.php.
Subdomain `api.example.com` with document root = project root: no extra rules; use https://api.example.com/v1/me.
Subdomain with document root = `/api` folder only: ship the repo’s `../.htaccess` in that folder. It maps /v1/… to v1/index.php and rewrites extensionless paths (e.g. /docs/cursor-skill-download) to *.php so public links match api_docs_url(). Older /docs/guide and /docs/reference URLs redirect to the unified docs hub.
Machine-readable contract: `openapi.yaml`. Human-friendly reference (examples + Try console) lives on the unified docs hub at `../index.php` (#ref-meta and section anchors).
Download the maintained Agent Skill for this API (implementation notes, auth, endpoints, error codes):
.cursor/skills/onestop-api/SKILL.md (or your tool’s equivalent skill folder).If these links 404, the server checkout is missing .cursor/skills/onestop-api/ (deploy with that directory or copy the files from the repo).
Replace YOUR_API_KEY and the base URL with yours. Production base is typically https://api.onestopinvoice.com/v1 (or your own host + /v1).
curl -sS -H "Authorization: Bearer YOUR_API_KEY" \
"https://api.onestopinvoice.com/v1/me"
You should see {"ok":true,"data":{...}} with account_id, currency, etc. If you see 401 / unauthorized, the key is wrong or missing.
Minimum JSON: name, phone, email are required. address and gstin are optional. Use gstin only when you have a valid 15-character GSTIN; otherwise send "" or omit.
curl -sS -X POST "https://api.onestopinvoice.com/v1/clients" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Retail Pvt Ltd",
"phone": "9876543210",
"email": "billing@acme.example",
"address": "Shop 12, MG Road, Bengaluru 560001",
"gstin": ""
}'
Response patterns:
data.skipped is false, and data.client.client_id is the id you need for invoices and quotations.200 with data.skipped: true and data.client set to the existing row. Always read client_id from this response before creating documents.List or find clients:
curl -sS -H "Authorization: Bearer YOUR_API_KEY" \
"https://api.onestopinvoice.com/v1/clients?limit=50&offset=0"
Each client object includes additive platform fields:
uses_onestopinvoice — true when the client matches another One Stop Invoice account(GSTIN first when present; if that fails or GSTIN is empty, business email)
onestopinvoice_logo_url — that account's business logo URL when available; null if unused or the matched account has no public logo URL (never a data: URI)
Both invoices and quotations accept an items array. Each element is one line. The server ignores any array entry where item_description is missing or empty after trim—so an “empty” {} or { "quantity": 1 } does nothing. Always set item_description.
| Field | Meaning |
|--------|---------|
| item_description | Required for the line to be saved (human-readable line text). |
| hsn_code | HSN/SAC string (can be ""). |
| quantity | Number (decimals allowed). |
| rate | Unit rate before tax (as you use in the app). |
| tax_rate | Percentage (e.g. 18 for 18%). |
| cgst_amount, sgst_amount, igst_amount | Tax amounts your integration calculated (intra-state often CGST+SGST; inter-state often IGST). |
| total_amount | Line total including tax (must match how your app/PDF logic works; the API stores what you send). |
| discount_type, discount_value | Optional; only if your database has discount columns (same as dashboard). discount_type: percentage or fixed. |
Inter-state example (IGST only, CGST/SGST zero): set igst_amount to your computed IGST and keep cgst_amount / sgst_amount at 0.
Required top-level fields: client_id, invoice_date, payment_due_date (dates as Y-m-d).
Optional: payment_status, status, currency, invoice_type, lut_number, declaration_text, items.
curl -sS -X POST "https://api.onestopinvoice.com/v1/invoices" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"client_id": 10,
"invoice_date": "2026-03-21",
"payment_due_date": "2026-04-05",
"payment_status": "pending",
"status": "draft",
"currency": "INR",
"invoice_type": "local_sale",
"items": [
{
"item_description": "Professional services March",
"hsn_code": "998314",
"quantity": 1,
"rate": 10000,
"tax_rate": 18,
"cgst_amount": 900,
"sgst_amount": 900,
"igst_amount": 0,
"total_amount": 11800
}
]
}'
Then fetch the full document with lines:
curl -sS -H "Authorization: Bearer YOUR_API_KEY" \
"https://api.onestopinvoice.com/v1/invoices/101"
GET /v1/invoices returns list rows only (no items). Use GET /v1/invoices/{id} for items[].
Required: client_id, quotation_date.
If you omit validity_date, the API defaults it to quotation_date.
items uses the same keys as invoice lines (see table above).
curl -sS -X POST "https://api.onestopinvoice.com/v1/quotations" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"client_id": 10,
"quotation_date": "2026-03-21",
"validity_date": "2026-04-21",
"approval_status": "pending",
"currency": "INR",
"quotation_type": "bill_of_supply",
"items": [
{
"item_description": "Annual maintenance (quoted)",
"hsn_code": "9987",
"quantity": 1,
"rate": 50000,
"tax_rate": 18,
"cgst_amount": 4500,
"sgst_amount": 4500,
"igst_amount": 0,
"total_amount": 59000
}
]
}'
Why it looked like “empty parentheses” before: sample docs used "items": []. That creates a valid quotation/invoice with no saved lines, because there are no rows with a non-empty item_description. Populate items as in the example above.
Debit notes use different item fields: description, expense_date, amount (not item_description / HSN).
"items": [
{ "description": "Rate difference adjustment", "expense_date": "2026-03-21", "amount": 500 }
]
Credit notes use description, expense_date, and amount per line (not invoice line fields). Optional source_invoice_id on create pre-fills from an existing invoice.
"items": [
{ "description": "Return adjustment", "expense_date": "2026-03-22", "amount": 500 }
]
Proforma invoices live on the dedicated proforma_invoices table (not the legacy document_kind: proforma path on quotations). Required: client_id, proforma_date, items[] with the same line shape as invoices.
curl -sS -X POST "https://api.onestopinvoice.com/v1/proforma-invoices" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"client_id": 10,
"proforma_date": "2026-03-22",
"validity_date": "2026-04-22",
"approval_status": "pending",
"items": [
{
"item_description": "Advance order",
"quantity": 1,
"rate": 25000,
"tax_rate": 18,
"cgst_amount": 2250,
"sgst_amount": 2250,
"igst_amount": 0,
"total_amount": 29500
}
]
}'
Convert to invoice: POST /v1/proforma-invoices/{id}/convert-to-invoice or POST /v1/quotations/{id}/convert-to-invoice.
| Action | Endpoint |
|--------|----------|
| Download PDF | GET /v1/documents/{type}/{id}/pdf — type is invoice, quotation, proforma-invoice, debit-note, or credit-note |
| Public share link | GET or POST /v1/documents/{type}/{id}/share |
| Revoke share | DELETE /v1/documents/{type}/{id}/share |
Send PDF: POST /v1/sendouts with client_id, document_type, document_id, subject, message.
Supported `document_type`: invoice, quotation, proforma-invoice, debit-note, credit-note.
History: GET /v1/sendouts?limit=20
Email template: GET /v1/sendouts/templates?document_type=invoice&client_id=10&document_id=101
Remove queue row: DELETE /v1/sendouts/{queue_id}
Activity alerts and login notifications for the Flutter app — no SMTP cost. Authenticate with mobile Bearer token (POST /v1/auth/login).
| Action | Endpoint |
|--------|----------|
| Sign in | POST /v1/auth/login — { username, password } → access_token, refresh_token |
| Refresh token | POST /v1/auth/refresh |
| Current user | GET /v1/auth/me — Bearer access_token |
| List feed | GET /v1/notifications?limit=50&offset=0 — optional unread_only=1 |
| Unread badge | GET /v1/notifications/unread-count |
| Mark read | PATCH /v1/notifications/{id}/read |
| Mark all read | POST /v1/notifications/read-all |
| Push prefs | GET / PATCH /v1/notifications/prefs |
| Register FCM | POST /v1/devices — { fcm_token, platform: android\|ios } |
| Unregister FCM | DELETE /v1/devices — { fcm_token } |
Each notification includes deep_link for navigation (invoice_detail, client_detail, etc.). Login alerts are hidden for the user who signed in (actor_user_id filter).
Note: Client invoice PDF delivery still uses POST /v1/sendouts (email). Push is for your account team only.
Summary: GET /v1/analytics/summary — dashboard aggregates. Optional ?fy_start_year=2025 scopes to a fiscal year; omit for all-time.
Full insights: GET /v1/analytics/full — extended metrics (expenses, inventory, TDS, credit notes, proforma, GST breakdown). Same optional fy_start_year.
| Group | Key endpoints |
|-------|----------------|
| TDS | GET/POST /v1/tds/sections, GET /v1/tds/deductions, PATCH /v1/tds/deductions/{id}/deposit, POST /v1/tds/certificates, GET /v1/tds/reports, POST /v1/tds/calculate |
| Settings | GET/PATCH /v1/settings/business, GET/POST/PATCH/DELETE /v1/settings/users/{id}, GET/PATCH /v1/settings/templates, GET/PATCH /v1/settings/ca-sync, POST /v1/settings/ca-sync/test-send, GET /v1/settings/api-key (masked, read-only) |
| Shopify | GET/POST/DELETE /v1/integrations/shopify, POST .../backfill, POST .../webhooks, GET .../logs |
Invoice create/update accepts optional TDS fields (tds_section_id, tds_rate, tds_amount) when your account uses TDS — same as the dashboard.
GET /v1/me confirm key and currency. POST /v1/clients upsert client; read client_id (handle skipped). POST /v1/quotations, POST /v1/proforma-invoices, or POST /v1/invoices with full items arrays. GET /v1/.../{id} verify stored lines and totals. GET /v1/documents/invoice/{id}/pdf download PDF, or POST /v1/sendouts email it. GET /v1/analytics/full?fy_start_year=2025 pull FY metrics for reporting.client_id, missing required dates, or wrong field names (e.g. debit note lines vs invoice lines). account_suspended or subscription_expired. Full error table: reference appendix.
/v1/me
Returns the account tied to your API key (id, business name, email, currency, GSTIN, PAN, subscription). PAN is the account business PAN (same for all documents).
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"account_id": 1,
"business_name": "Demo Traders",
"business_email": "hello@example.com",
"currency": "INR",
"business_gstin": "27AAAAA0000A1Z5",
"business_pan": "AAAAA9999A",
"subscription_expires_at": "2026-12-31 23:59:59"
}
}
/v1/clients
Paginated clients. Query: limit (max 200, default 50), offset.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"clients": [
{
"client_id": 10,
"name": "Ravi Kumar",
"phone": "9876543210",
"email": "ravi@client.test",
"gstin": null,
"uses_onestopinvoice": false,
"onestopinvoice_logo_url": null
}
],
"total": 1
}
}
/v1/clients
Create client (name, phone, email required). Optional: address, gstin (15-char format when used). Duplicate phone (10 digits), email, or GSTIN → 200 with data.skipped true and data.client = existing row.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"skipped": false,
"client": {
"client_id": 11,
"name": "Acme Retail Pvt Ltd",
"phone": "9876543210",
"email": "billing@acme.example",
"address": "Shop 12, MG Road, Bengaluru 560001",
"gstin": null,
"state_code": null,
"uses_onestopinvoice": false,
"onestopinvoice_logo_url": null
}
}
}
/v1/clients/{id}
Single client by id. Includes uses_onestopinvoice + onestopinvoice_logo_url when GSTIN (or email if no GSTIN) matches another account.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"client_id": 10,
"name": "Ravi Kumar",
"phone": "9876543210",
"email": "ravi@client.test",
"uses_onestopinvoice": false,
"onestopinvoice_logo_url": null
}
}
/v1/clients/{id}
Partial update (JSON merged).
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"client_id": 10,
"name": "Ravi K. (updated)"
}
}
/v1/clients/{id}
Delete client. Query cascade=1 to remove related invoices, quotations, proforma invoices, debit notes, and credit notes.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"deleted": true
}
}
/v1/invoices
List invoices. Query: limit, offset, client_id, fy_start_year, search, status, payment_status, due_from, due_to, payment_date_from, payment_date_to. Rows are DB columns (no line items on list).
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"invoices": [
{
"invoice_id": 101,
"client_id": 10,
"invoice_number": "INV-2026-0042",
"invoice_date": "2026-03-21",
"payment_due_date": "2026-04-05",
"payment_status": "pending",
"status": "draft",
"currency": "INR"
}
],
"total": 1
}
}
/v1/invoices
Create invoice. Required: client_id, invoice_date, payment_due_date (Y-m-d). Each items[] row needs item_description (non-empty) or the row is skipped. Optional inventory_id on a line links to a product and deducts stock when saved. Send tax splits your app already calculated (CGST+SGST for intra-state, or IGST). Optional: transport_details, lr_number (per document). Business PAN is not on the invoice; use GET /me business_pan. Optional: discount_type percentage|fixed + discount_value when DB supports it.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"invoice_id": 101,
"message": "created"
}
}
/v1/invoices/{id}
Single invoice: header fields from invoices row plus items[] (invoice_items columns, ordered by item_id).
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"invoice_id": 101,
"client_id": 10,
"invoice_number": "INV-2026-0042",
"invoice_date": "2026-03-21",
"payment_due_date": "2026-04-05",
"currency": "INR",
"items": [
{
"item_id": 501,
"invoice_id": 101,
"item_description": "Professional services March",
"hsn_code": "998314",
"quantity": 1,
"rate": 10000,
"tax_rate": 18,
"cgst_amount": 900,
"sgst_amount": 900,
"igst_amount": 0,
"total_amount": 11800
}
]
}
}
/v1/invoices/{id}
Partial update.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"invoice_id": 101
}
}
/v1/invoices/{id}
Delete invoice.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"deleted": true
}
}
/v1/invoices/bulk-delete
Bulk delete invoices. Body: {"ids": [101, 102]}.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"deleted": 2,
"skipped": []
}
}
/v1/invoices/bulk-pdf
Download multiple invoice PDFs as ZIP (binary response). Body: {"ids": [101, 102]}.
Curl uses production base · key must match that environment
{
"note": "Returns application/zip attachment."
}
/v1/invoices/recent-payments
Invoices marked paid since ?since= timestamp (default: last hour).
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"payments": []
}
}
/v1/quotations
List quotations. Query: limit, offset, client_id, fy_start_year, search, approval_status, document_kind. Rows omit line items (use GET /quotations/{id} for items[]) but include total_amount (sum of line items).
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"quotations": [
{
"quotation_id": 55,
"client_id": 10,
"quotation_number": "QT-2026-0007",
"quotation_date": "2026-03-21",
"validity_date": "2026-04-21",
"approval_status": "pending",
"currency": "INR",
"total_amount": 11800
}
],
"total": 1
}
}
/v1/quotations
Create quotation. Required: client_id, quotation_date. validity_date defaults to quotation_date if omitted. items[] uses the same keys as invoice line items; each row must have item_description or it is ignored. Optional: transport_details, lr_number (per document). Business PAN from GET /me.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"quotation_id": 55
}
}
/v1/quotations/{id}
Single quotation header plus items[] (quotation_items columns).
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"quotation_id": 55,
"client_id": 10,
"quotation_number": "QT-2026-0007",
"quotation_date": "2026-03-21",
"validity_date": "2026-04-21",
"currency": "INR",
"items": [
{
"item_id": 8801,
"quotation_id": 55,
"item_description": "Annual maintenance (quoted)",
"hsn_code": "9987",
"quantity": 1,
"rate": 50000,
"tax_rate": 18,
"cgst_amount": 4500,
"sgst_amount": 4500,
"igst_amount": 0,
"total_amount": 59000
}
]
}
}
/v1/quotations/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"quotation_id": 55
}
}
/v1/quotations/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"deleted": true
}
}
/v1/debit-notes
List debit notes. Query: limit, offset, client_id.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"debit_notes": [
{
"debit_note_id": 7,
"client_id": 10,
"debit_note_number": "DN-3",
"debit_note_date": "2026-03-21",
"currency": "INR"
}
],
"total": 1
}
}
/v1/debit-notes
Create debit note with line items. Optional: transport_details, lr_number (per document). Business PAN from GET /me.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"debit_note_id": 7
}
}
/v1/debit-notes/{id}
Debit note with items[] (description, expense_date, amount per line).
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"debit_note_id": 7,
"client_id": 10,
"debit_note_date": "2026-03-21",
"currency": "INR",
"items": [
{
"item_id": 120,
"debit_note_id": 7,
"description": "Rate difference adjustment",
"expense_date": "2026-03-21",
"amount": 500
}
]
}
}
/v1/debit-notes/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"debit_note_id": 7
}
}
/v1/debit-notes/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"deleted": true
}
}
/v1/credit-notes
List credit notes. Supports fy_start_year, search, status, client_id, and date filters.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"credit_notes": [
{
"credit_note_id": 12,
"client_id": 10,
"credit_note_number": "CN-4",
"credit_note_date": "2026-03-22",
"currency": "INR"
}
],
"total": 1
}
}
/v1/credit-notes
Create credit note. Optional source_invoice_id pre-fills from an invoice.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"credit_note_id": 12
}
}
/v1/credit-notes/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"credit_note_id": 12,
"items": []
}
}
/v1/credit-notes/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"credit_note_id": 12
}
}
/v1/credit-notes/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"deleted": true
}
}
/v1/proforma-invoices
List proforma invoices from proforma_invoices table. Supports FY and approval filters.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"proforma_invoices": [
{
"proforma_invoice_id": 8,
"client_id": 10,
"proforma_number": "PI-2026-0003",
"proforma_date": "2026-03-22",
"approval_status": "pending"
}
],
"total": 1
}
}
/v1/proforma-invoices
Create proforma invoice. Required: client_id, proforma_date, items[].
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"proforma_invoice_id": 8
}
}
/v1/proforma-invoices/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"proforma_invoice_id": 8,
"items": []
}
}
/v1/proforma-invoices/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"proforma_invoice_id": 8
}
}
/v1/proforma-invoices/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"deleted": true
}
}
/v1/proforma-invoices/{id}/convert-to-invoice
Convert proforma to a new invoice.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"invoice_id": 102
}
}
/v1/documents/{type}/{id}/pdf
Download PDF binary. type uses URL slug (proforma-invoice with hyphen).
Curl uses production base · key must match that environment
{
"note": "Returns application/pdf attachment, not JSON envelope."
}
/v1/documents/{type}/{id}/share
Get or create public share link.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"share_url": "https://app.onestopinvoice.com/share/abc123"
}
}
/v1/documents/{type}/{id}/share
Revoke public share link.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"revoked": true
}
}
/v1/quotations/{id}/convert-to-invoice
Convert quotation to invoice.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"invoice_id": 103
}
}
/v1/inventory
List inventory rows (products/services).
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"items": [
{
"inventory_id": 3,
"name": "Widget A",
"sku": "OSI-00003",
"barcode_type": "CODE128",
"type": "product",
"rate": 250,
"quantity": 100,
"tax_rate": 18,
"hsn_code": "1234"
}
],
"total": 1
}
}
/v1/inventory
Create product/service line.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"inventory_id": 3
}
}
/v1/inventory/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"inventory_id": 3,
"name": "Widget A"
}
}
/v1/inventory/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"inventory_id": 3
}
}
/v1/inventory/{id}/movements
Stock movement history for one inventory item.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"movements": [
{
"movement_id": 12,
"quantity_delta": -2,
"quantity_after": 98,
"source_type": "invoice",
"source_label": "Invoice #104",
"note": "Stock deducted — invoice saved"
}
]
}
}
/v1/inventory/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"deleted": true
}
}
/v1/expenses/categories
Static list of expense categories (same as dashboard).
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"categories": [
"General",
"Travel",
"Office"
]
}
}
/v1/expenses/{id}/repeat
Get expense row prefilled for repeat entry.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"expense_id": 9,
"expense_description": "Office supplies"
}
}
/v1/expenses
List expenses.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"expenses": [
{
"expense_id": 9,
"expense_description": "Office supplies",
"expense_amount": 1200,
"expense_date": "2026-03-20",
"expense_category": "General"
}
],
"total": 1
}
}
/v1/expenses
Create expense.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"expense_id": 9
}
}
/v1/expenses/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"expense_id": 9
}
}
/v1/expenses/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"expense_id": 9
}
}
/v1/expenses/{id}
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"deleted": true
}
}
/v1/sendouts
List sendout delivery history.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"history": [
{
"queue_id": 42,
"document_type": "invoice",
"document_id": 101,
"status": "sent"
}
]
}
}
/v1/sendouts/templates
Default email template for a document. Query: document_type, client_id, document_id.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"subject": "Invoice #101",
"message": "Please find attached."
}
}
/v1/sendouts/documents
List documents for a client and type. Query: document_type, client_id.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"documents": [
{
"document_id": 101,
"label": "Invoice #101"
}
]
}
}
/v1/sendouts
Queue email with PDF. document_type: invoice, quotation, proforma-invoice, debit-note, credit-note.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"success": true,
"message": "Email queued",
"queue_id": 42
}
}
/v1/sendouts/{queue_id}
Remove a queued sendout record.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"deleted": true
}
}
/v1/auth/login
Mobile sign-in (no API key). Returns access_token and refresh_token. Use Bearer access_token for notifications and device registration below.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"access_token": "a1b2c3…",
"refresh_token": "d4e5f6…",
"expires_at": "2026-07-12 11:00:00",
"refresh_expires_at": "2026-08-11 10:00:00",
"user": {
"user_id": 3,
"account_id": 1,
"username": "admin",
"business_email": "hello@example.com"
},
"permissions": [
"invoices",
"clients"
],
"business_name": "Demo Traders"
}
}
/v1/auth/refresh
Rotate mobile access token using refresh_token.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"access_token": "new-token…",
"refresh_token": "new-refresh…",
"expires_at": "2026-07-12 12:00:00",
"refresh_expires_at": "2026-09-10 10:00:00"
}
}
/v1/auth/logout
Revoke refresh token.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"logged_out": true
}
}
/v1/auth/me
Current mobile user (requires Bearer access_token).
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"user_id": 3,
"account_id": 1,
"username": "admin",
"business_email": "hello@example.com",
"business_name": "Demo Traders",
"permissions": [
"invoices",
"clients"
]
}
}
/v1/notifications
In-app notification feed for account activity and logins. Bearer access_token recommended (filters login alerts for the signing-in user). Query: limit (max 100), offset, unread_only=1.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"notifications": [
{
"notification_id": 12,
"type": "activity",
"category": "invoices",
"action": "created",
"title": "Invoice INV-101 created",
"body": "admin created Invoice INV-101.",
"entity_type": "invoice",
"entity_id": 101,
"actor_user_id": 3,
"read_at": null,
"created_at": "2026-07-12 10:00:00",
"deep_link": {
"screen": "invoice_detail",
"entity_type": "invoice",
"entity_id": 101,
"document_type": "invoice",
"document_id": 101
}
}
],
"unread_count": 1,
"limit": 50,
"offset": 0
}
}
/v1/notifications/unread-count
Unread badge count for the mobile app.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"unread_count": 3
}
}
/v1/notifications/{id}/read
Mark one notification as read.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"notification": {
"notification_id": 12,
"read_at": "2026-07-12 10:05:00"
}
}
}
/v1/notifications/read-all
Mark all visible notifications as read.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"marked_read": 3
}
}
/v1/notifications/prefs
Push notification preferences (all categories default to true).
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"prefs": {
"login": true,
"invoices": true,
"clients": true
}
}
}
/v1/notifications/prefs
Update push notification preferences.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"saved": true,
"prefs": {
"login": true,
"invoices": true,
"clients": false
}
}
}
/v1/devices
Register FCM device token (mobile Bearer required). platform: android or ios.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"device_id": 7,
"registered": true
}
}
/v1/devices
Unregister FCM token on logout.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"unregistered": true
}
}
/v1/analytics/summary
Dashboard aggregates. Optional ?fy_start_year= for FY scoping.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"total_invoices": 12,
"total_revenue": 450000,
"fy_start_year": 2025
}
}
/v1/analytics/full
Extended insights: expenses, inventory, TDS, credit notes, proforma, GST breakdown.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"fy_start_year": 2025,
"invoices": {
"total": 12
},
"expenses": {
"total": 85000
},
"inventory": {
"enabled": true,
"summary": {
"items": 24,
"products": 18,
"services": 6,
"stock_value": 1250000,
"units_sold_fy": 420
},
"top_sold_products": []
}
}
}
/v1/tds/sections
List TDS sections.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"sections": []
}
}
/v1/tds/deductions
List deductions. Optional ?fy_start_year=.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"deductions": []
}
}
/v1/tds/reports
FY TDS reports.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"summary": []
}
}
/v1/tds/calculate
Calculate TDS amount for a given base and section.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"tds_amount": 1000
}
}
/v1/settings/business
Business profile and payment info.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"business_name": "Acme Pvt Ltd"
}
}
/v1/settings/users
List sub-users.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"users": []
}
}
/v1/settings/templates
Document template preferences.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"invoice_template": "classic"
}
}
/v1/settings/ca-sync
CA sync schedule configuration.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"enabled": false
}
}
/v1/settings/api-key
Masked API key (read-only).
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"api_key_masked": "osi_****abcd"
}
}
/v1/integrations/shopify
Shopify connection status.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"connected": false
}
}
/v1/integrations/shopify/logs
Shopify sync logs.
Curl uses production base · key must match that environment
{
"ok": true,
"data": {
"logs": []
}
}