openapi: 3.0.3 info: title: One Stop Invoice API version: "1.0.0" description: | Full-account JSON API. Authenticate with the API key from Dashboard → API Key. Production base: `https://api.onestopinvoice.com/v1`. Same codebase may serve `/v1` on the app host locally (see `.htaccess`). servers: - url: https://api.onestopinvoice.com/v1 description: Production API - url: /v1 description: Same origin (local development) tags: - name: Meta - name: Clients - name: Invoices - name: Quotations - name: DebitNotes - name: Inventory - name: Expenses - name: Sendouts - name: Analytics components: securitySchemes: ApiKeyHeader: type: apiKey in: header name: X-API-Key BearerAuth: type: http scheme: bearer bearerFormat: hex schemas: ErrorEnvelope: type: object required: [ok, error] properties: ok: type: boolean example: false error: type: object properties: code: { type: string } message: { type: string } details: { type: object, additionalProperties: true } Client: type: object properties: client_id: { type: integer } name: { type: string } phone: { type: string } email: { type: string } address: { type: string, nullable: true } gstin: { type: string, nullable: true } state_code: { type: string, nullable: true } created_at: { type: string, nullable: true } updated_at: { type: string, nullable: true } Invoice: type: object additionalProperties: true description: Row from `invoices` plus `items` array on detail responses. InvoiceItem: type: object description: > Lines without item_description (or empty after trim) are skipped on create. Same object is used for invoice and quotation line items. properties: item_description: { type: string } hsn_code: { type: string } quantity: { type: number } rate: { type: number } tax_rate: { type: number } cgst_amount: { type: number } sgst_amount: { type: number } igst_amount: { type: number } total_amount: { type: number } discount_type: { type: string, nullable: true, enum: [percentage, fixed] } discount_value: { type: number, nullable: true } example: 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 Quotation: type: object additionalProperties: true DebitNote: type: object additionalProperties: true DebitNoteItem: type: object description: Debit note lines (not the same fields as InvoiceItem). properties: description: { type: string } expense_date: { type: string, format: date } amount: { type: number } example: description: Rate difference adjustment expense_date: "2026-03-21" amount: 500 security: - ApiKeyHeader: [] - BearerAuth: [] paths: /me: get: tags: [Meta] summary: Current account (from API key) responses: "200": description: OK content: application/json: schema: type: object properties: ok: { type: boolean, example: true } data: type: object properties: account_id: { type: integer } business_name: { type: string, nullable: true } business_email: { type: string, nullable: true } currency: { type: string } subscription_expires_at: { type: string, nullable: true } /clients: get: tags: [Clients] summary: List clients parameters: - in: query name: limit schema: { type: integer, default: 50, maximum: 200 } - in: query name: offset schema: { type: integer, default: 0 } responses: "200": description: Paginated list content: application/json: schema: type: object properties: ok: { type: boolean } data: type: object properties: clients: type: array items: { $ref: "#/components/schemas/Client" } total: { type: integer } post: tags: [Clients] summary: Create client (or skip duplicate) requestBody: required: true content: application/json: schema: type: object required: [name, phone, email] properties: name: { type: string } phone: { type: string, description: 10 digits } email: { type: string, format: email } address: { type: string } gstin: { type: string } example: name: Acme Retail Pvt Ltd phone: "9876543210" email: billing@acme.example address: Shop 12, MG Road, Bengaluru 560001 gstin: "" responses: "200": description: Created or skipped content: application/json: schema: type: object properties: ok: { type: boolean } data: type: object properties: skipped: { type: boolean } client: { $ref: "#/components/schemas/Client" } /clients/{id}: get: tags: [Clients] summary: Get client parameters: - in: path name: id required: true schema: { type: integer } responses: "200": description: OK "404": { description: Not found } patch: tags: [Clients] summary: Update client parameters: - in: path name: id required: true schema: { type: integer } requestBody: content: application/json: schema: type: object required: [name, phone, email] properties: name: { type: string } phone: { type: string } email: { type: string } address: { type: string } gstin: { type: string } responses: "200": { description: OK } delete: tags: [Clients] summary: Delete client parameters: - in: path name: id required: true schema: { type: integer } - in: query name: cascade schema: { type: string, enum: ["1", "true"] } description: Delete related invoices, quotations, debit notes, clear expense links responses: "200": { description: Deleted } "409": { description: Has related records without cascade } /invoices: get: tags: [Invoices] summary: List invoices parameters: - in: query name: limit schema: { type: integer } - in: query name: offset schema: { type: integer } - in: query name: client_id schema: { type: integer } responses: "200": { description: OK } post: tags: [Invoices] summary: Create invoice requestBody: content: application/json: schema: type: object required: [client_id, invoice_date, payment_due_date] properties: client_id: { type: integer } invoice_date: { type: string, format: date } payment_due_date: { type: string, format: date } payment_status: { type: string, enum: [pending, paid, partial, overdue] } status: { type: string, enum: [draft, issued, cancelled, archived] } currency: { type: string } invoice_type: { type: string } lut_number: { type: string, nullable: true } declaration_text: { type: string, nullable: true } paid_amount: { type: number } payment_date: { type: string, format: date, nullable: true } items: type: array items: { $ref: "#/components/schemas/InvoiceItem" } example: 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 responses: "201": { description: Created } /invoices/{id}: get: tags: [Invoices] parameters: - in: path name: id required: true schema: { type: integer } responses: "200": { description: Invoice with items } patch: tags: [Invoices] summary: Update invoice (partial JSON merged over existing) parameters: - in: path name: id required: true schema: { type: integer } requestBody: content: application/json: schema: type: object additionalProperties: true responses: "200": { description: OK } delete: tags: [Invoices] parameters: - in: path name: id required: true schema: { type: integer } responses: "200": { description: Deleted } /quotations: get: tags: [Quotations] summary: List quotations parameters: - in: query name: limit schema: { type: integer } - in: query name: offset schema: { type: integer } - in: query name: client_id schema: { type: integer } responses: "200": { description: OK } post: tags: [Quotations] summary: Create quotation requestBody: content: application/json: schema: type: object required: [client_id, quotation_date] properties: client_id: { type: integer } quotation_date: { type: string, format: date } validity_date: { type: string, format: date } approval_status: { type: string, enum: [pending, approved, rejected] } currency: { type: string } quotation_type: { type: string } lut_number: { type: string, nullable: true } declaration_text: { type: string, nullable: true } items: type: array items: { $ref: "#/components/schemas/InvoiceItem" } example: 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 responses: "201": { description: Created } /quotations/{id}: get: tags: [Quotations] parameters: - in: path name: id required: true schema: { type: integer } responses: "200": { description: OK } patch: tags: [Quotations] parameters: - in: path name: id required: true schema: { type: integer } requestBody: content: application/json: schema: { type: object, additionalProperties: true } responses: "200": { description: OK } delete: tags: [Quotations] parameters: - in: path name: id required: true schema: { type: integer } responses: "200": { description: Deleted } /debit-notes: get: tags: [DebitNotes] summary: List debit notes parameters: - in: query name: limit schema: { type: integer } - in: query name: offset schema: { type: integer } - in: query name: client_id schema: { type: integer } responses: "200": { description: OK } post: tags: [DebitNotes] summary: Create debit note requestBody: content: application/json: schema: type: object required: [client_id, debit_note_date] properties: client_id: { type: integer } debit_note_date: { type: string, format: date } payment_due_date: { type: string, format: date } declaration_text: { type: string } currency: { type: string } status: { type: string } payment_status: { type: string } paid_amount: { type: number } payment_date: { type: string, format: date, nullable: true } items: type: array items: { $ref: "#/components/schemas/DebitNoteItem" } example: client_id: 10 debit_note_date: "2026-03-21" payment_due_date: "2026-04-01" currency: INR items: - description: Rate difference adjustment expense_date: "2026-03-21" amount: 500 responses: "201": { description: Created } /debit-notes/{id}: get: tags: [DebitNotes] parameters: - in: path name: id required: true schema: { type: integer } responses: "200": { description: OK } patch: tags: [DebitNotes] parameters: - in: path name: id required: true schema: { type: integer } requestBody: content: application/json: schema: { type: object, additionalProperties: true } responses: "200": { description: OK } delete: tags: [DebitNotes] parameters: - in: path name: id required: true schema: { type: integer } responses: "200": { description: Deleted } /inventory: get: tags: [Inventory] parameters: - in: query name: limit schema: { type: integer } - in: query name: offset schema: { type: integer } responses: "200": { description: OK } post: tags: [Inventory] requestBody: content: application/json: schema: type: object required: [name, rate] properties: name: { type: string } type: { type: string, enum: [product, service] } description: { type: string } quantity: { type: integer } rate: { type: number } discount_type: { type: string, nullable: true } discount_value: { type: number } tax_rate: { type: number } hsn_code: { type: string } responses: "201": { description: Created } /inventory/{id}: get: tags: [Inventory] parameters: - in: path name: id required: true schema: { type: integer } responses: "200": { description: OK } patch: tags: [Inventory] parameters: - in: path name: id required: true schema: { type: integer } requestBody: content: application/json: schema: { type: object, additionalProperties: true } responses: "200": { description: OK } delete: tags: [Inventory] parameters: - in: path name: id required: true schema: { type: integer } responses: "200": { description: Deleted } /expenses: get: tags: [Expenses] parameters: - in: query name: limit schema: { type: integer } - in: query name: offset schema: { type: integer } responses: "200": { description: OK } post: tags: [Expenses] requestBody: content: application/json: schema: type: object required: [expense_description, expense_amount] properties: expense_description: { type: string } expense_category: { type: string } vendor_name: { type: string } vendor_contact: { type: string } expense_amount: { type: number } expense_date: { type: string, format: date } expense_status: { type: string } payment_method: { type: string } receipt_url: { type: string } notes: { type: string } client_id: { type: integer, nullable: true } responses: "201": { description: Created } /expenses/{id}: get: tags: [Expenses] parameters: - in: path name: id required: true schema: { type: integer } responses: "200": { description: OK } patch: tags: [Expenses] parameters: - in: path name: id required: true schema: { type: integer } requestBody: content: application/json: schema: { type: object, additionalProperties: true } responses: "200": { description: OK } delete: tags: [Expenses] parameters: - in: path name: id required: true schema: { type: integer } responses: "200": { description: Deleted } /sendouts: post: tags: [Sendouts] summary: Email PDF for invoice, quotation, or debit-note requestBody: content: application/json: schema: type: object required: [client_id, document_type, document_id, subject, message] properties: client_id: { type: integer } document_type: { type: string, enum: [invoice, quotation, debit-note] } document_id: { type: integer } to_email: { type: string, format: email, description: Optional if client has email } cc_emails: { type: string, description: Comma-separated } bcc_emails: { type: string } subject: { type: string, maxLength: 500 } message: { type: string } signature_override: type: string description: Base64 PNG or data URL when account has no stored signature responses: "200": description: Sent content: application/json: schema: type: object properties: ok: { type: boolean } data: type: object properties: success: { type: boolean } message: { type: string } queue_id: { type: integer } /analytics/summary: get: tags: [Analytics] summary: Dashboard-style aggregates responses: "200": description: Same shape as legacy `app/api/get-analytics.php` JSON