mb mybillbook private beta
← API overview

API reference

Selected high-traffic endpoints with their authentication requirements, request shape, CSRF need, and response contract. Every endpoint listed is in production code on the main branch.

Middleware stack

Order matters. Inbound requests traverse top to bottom:

  1. 1. RequestID
    Generates a request_id UUID per inbound request and threads it through slog + audit.
  2. 2. Recovery
    Recovers from panics, logs with full stack trace + request_id, returns 500.
  3. 3. Slog
    Structured JSON logging of method, path, status, duration, request_id, tenant_id.
  4. 4. RateLimit
    Per-IP sliding-window limit. Postgres-backed in non-local envs (multi-instance safe).
  5. 5. CSRF
    Double-submit cookie token, constant-time compared on every mutating request.
  6. 6. Auth
    Validates session cookie, attaches user_id + tenant_id to context, redirects to /auth/signin on miss.
  7. 7. Idempotency
    Honors Idempotency-Key header on POST /invoices, /payments, /suppliers, /purchases. 24h sweep + 5-min stuck-row reclaim.
  8. 8. Audit
    Captures actor + IP + UA + request_id into ctx for service-layer RecordTx calls.

Endpoints

POST /auth/signup
Auth: none + rate-limit 5/hour/IP

Create a user + tenant + initial owner membership + email-verification token in one transaction. Sends a verification email. Audit-logged with IP + User-Agent for DPDP §8(8).

Fields
  • · email (string, lowercase)
  • · password (≥10 chars)
Returns: 303 → /auth/verify-pending on success; 400 with X-Form-Validation-Error: 1 + form-rerender HTML on validation failure.
POST /auth/signin CSRF
Auth: none + rate-limit 10/5min/IP

Verify credentials with argon2id constant-time compare. Returns generic ErrInvalidCredentials for both unknown-email and wrong-password to prevent enumeration. Creates session row, sets HttpOnly+Secure session cookie.

Fields
  • · email
  • · password
  • · _csrf (double-submit)
Returns: 303 → /dashboard on success.
POST /invoices CSRF
Auth: session + active business

Create a draft invoice. Server-side compute of CGST/SGST/IGST from supplier state vs place-of-supply. Lines persisted with HSN, qty, rate, discount, tax_rate, taxable, per-tax money fields, total.

Fields
  • · customer_id (uuid)
  • · issue_date (YYYY-MM-DD)
  • · due_date (YYYY-MM-DD)
  • · place_of_supply (2-digit state code)
  • · reverse_charge (bool)
  • · line_hsn_sac[], line_description[], line_qty[], line_unit[], line_rate[], line_discount[], line_tax_rate[] (parallel arrays)
  • · Idempotency-Key header (optional)
Returns: 303 → /invoices/{id}
POST /invoices/{id}/finalize CSRF
Auth: session + active business

Flip a draft → finalized. Allocates the gapless per-FY invoice number via SELECT FOR UPDATE on invoice_counters. Writes JSONB snapshot for 6-year retention. Enqueues render_pdf job.

Fields
  • · _csrf
Returns: 303 → /invoices/{id} with notice. ErrAlreadyFinalized if double-clicked.
POST /invoices/{id}/cancel CSRF
Auth: session + active business

Cancel a finalized invoice. ErrCannotCancelPaid if any payment is allocated to it. Audit-logged.

Fields
  • · _csrf
Returns: 303 → /invoices/{id} or 400 with error message.
POST /payments CSRF
Auth: session + active business

Record a payment + allocations across one or more invoices in a single tx. Allocation sum ≤ payment amount enforced server-side. Supports Idempotency-Key for retry safety.

Fields
  • · customer_id, amount, currency (INR default), mode, reference_no, paid_at
  • · allocation_invoice_id[], allocation_amount[] (parallel arrays)
  • · Idempotency-Key header
Returns: 303 → /payments/{id}
POST /quotations/{id}/send CSRF
Auth: session + active business

Flip draft → sent. Issues a 256-bit random share token (crypto/rand → hex), persists to quotations.public_token. Audit-logged with the supplier IP + UA so future replays can be traced.

Fields
  • · _csrf
Returns: 303 → /quotations/{id} with the share URL in flash.
POST /quotations/{id}/convert CSRF
Auth: session + active business

Convert an accepted quotation to a draft invoice atomically. Creates the invoice with the same lines + customer + place-of-supply, stamps quotation.converted_to_invoice_id, transitions quote to 'converted' state. Single tx.

Fields
  • · _csrf
Returns: 303 → /invoices/{new_id}
GET /q/{token}
Auth: PUBLIC (no auth, rate-limited 20/min/IP)

Customer-facing read-only view of a quote. Token regex-validated at the HTTP boundary BEFORE the DB lookup. Returns 404 indistinguishably for rejected and unknown tokens (privacy: prevents enumeration of valid-but-rejected URLs). No cookies set on this response.

Returns: 200 HTML with X-Robots-Tag: noindex, Cache-Control: private, no-store, Referrer-Policy: same-origin.
POST /gstr2b/upload CSRF
Auth: session + active business

Multipart upload of a CBIC v3.0 GSTR-2B JSON. Parses strictly; counts skipped non-b2b sections; persists one gstr2b_imports row + N gstr2b_invoices rows in a single tx. Period collision returns ErrPeriodExists; re-upload with ?replace=1 triggers the replace flow.

Fields
  • · file (multipart, max 5 MB)
  • · period (YYYY-MM)
Returns: 303 → /gstr2b/{import_id}
GET /gstr2b/{import_id}
Auth: session + active business

The reconciliation page. Pulls books-side purchases for the period, runs gstr2b.Match against the persisted portal rows, groups by bucket, renders the 4-tab UI. Default tab: issues.

Fields
  • · ?tab=matched|issues|books|portal
Returns: 200 HTML
POST /gstr2b/invoices/{id}/mark CSRF
Auth: session + active business

HTMX endpoint. Sets gstr2b_invoices.user_marked to one of {ignore, dispute, clear}. Returns the updated <tr> row HTML for in-place swap. Audit-logged.

Fields
  • · ?action=ignore|dispute|clear
Returns: 200 HTML fragment (a <tr>).
GET /gstr1/download.json
Auth: session + active business

Returns the CBIC v3.0 GSTR-1 JSON for the active period. Schema includes B2B / B2CL / B2CS / CDNR / CDNUR sections + HSN summary. Same bytes the offline tool expects.

Fields
  • · ?period=YYYY-MM (defaults to current month)
Returns: 200 application/json + Content-Disposition: attachment

This is a curated subset. The full ~70 routes are listed at /api grouped by domain. Drop us an email if you'd like deeper docs on a specific endpoint.