Authentication

The API supports two authentication methods:

1. API Key (for external integrations & subsidiaries)

Pass your API key in the x-api-key header. API keys can be generated from the Dashboard under Payments & API.

// Example with API key curl -X POST https://akshaykotish.com/api/payments/api/record \ -H "x-api-key: ak_your_api_key_here" \ -H "Content-Type: application/json" \ -d '{"amount": 5000, "type": "incoming", "description": "Product sale"}'

2. Bearer Token (for dashboard/internal use)

Firebase Auth tokens for authenticated dashboard users.

// Example with Bearer token curl -X GET https://akshaykotish.com/api/billing/invoices \ -H "Authorization: Bearer <firebase_id_token>"

Base URL

https://akshaykotish.com/api

All API endpoints are relative to this base URL.

Record Payment

Record a payment directly via API key. Used by subsidiaries and external tools.

POST /payments/api/record API Key
Records an incoming or outgoing payment and creates a journal entry.

Headers

NameTypeRequiredDescription
x-api-keystringRequiredYour API key
Content-TypestringRequiredapplication/json

Body Parameters

NameTypeRequiredDescription
amountnumberRequiredPayment amount in INR
typestringincoming or outgoing (default: incoming)
methodstringbank_transfer, cash, upi, cheque, other
descriptionstringPayment description
referencestringExternal reference ID
invoiceIdstringLink to an existing invoice (marks it as paid)
metadataobjectAny additional metadata

Response

{ "success": true, "paymentId": "PAY-A1B2C3D4", "id": "firestore_doc_id" }

Create Razorpay Order

Create a Razorpay order for payment capture. Returns the order ID and key for client-side checkout.

POST /razorpay/api/capture API Key

Body Parameters

NameTypeRequiredDescription
amountnumberRequiredAmount in INR (e.g., 500.00)
currencystringCurrency code (default: INR)
descriptionstringOrder description
customerobject{ name, email, phone }
invoiceIdstringLink to invoice
callbackUrlstringURL to redirect after payment

Response

{ "success": true, "orderId": "order_xxxxxxxxxxxxx", "amount": 500, "currency": "INR", "keyId": "rzp_live_xxxxx", "recordId": "firestore_doc_id" }

Client-Side Integration

// Use the returned orderId and keyId with Razorpay Checkout const options = { key: response.keyId, order_id: response.orderId, amount: response.amount * 100, currency: response.currency, name: "Your App Name", handler: function(resp) { // Verify payment on your server fetch('/api/razorpay/verify', { method: 'POST', body: JSON.stringify(resp) }); } }; const rzp = new Razorpay(options); rzp.open();

Razorpay Webhook

Configure this URL in your Razorpay Dashboard under Settings > Webhooks to receive automatic payment status updates.

POST /razorpay/webhook Public
Automatically processes payment.captured and payment_link.paid events. Updates payment records and linked invoices.
Setup: In your Razorpay Dashboard, add the webhook URL https://akshaykotish.com/api/razorpay/webhook and select the events: payment.captured, payment_link.paid.

Create Invoice

Create a GST-compliant invoice with automatic tax calculations.

POST /billing/invoices Bearer Token

Body Parameters

NameTypeRequiredDescription
customerobjectRequired{ name, email, phone, address, gstin, state }
itemsarrayRequired[{ description, hsn, qty, rate, gstRate }]
notesstringInvoice notes
dueDatestringDue date (ISO format, default: 30 days)

Response

{ "id": "firestore_doc_id", "invoiceNumber": "INV-2026-0001", "subtotal": 10000, "cgst": 900, "sgst": 900, "total": 11800, "status": "draft" }
GST Auto-Calculation: If the customer's state matches the company state (Haryana), CGST + SGST are applied. For interstate customers, IGST is applied instead.

List Invoices

GET /billing/invoices Bearer Token
Returns all invoices ordered by creation date (newest first).

Send Email

POST /mail/send Auth Required

Send an email via the configured SMTP provider (Gmail, Brevo, or custom SMTP).

Request Body

FieldTypeRequiredDescription
tostringYesRecipient email address
subjectstringYesEmail subject line
htmlstringYesHTML body of the email
fromAliasstringNoSend from a specific alias email address
fromNamestringNoDisplay name for the sender
attachmentsarrayNoArray of nodemailer attachment objects

Example

POST /api/mail/send { "to": "client@example.com", "subject": "Invoice INV-2026-0001", "html": "<h1>Invoice</h1><p>Please find your invoice...</p>", "fromAlias": "billing@akshaykotish.com", "fromName": "AK & Co. Billing" }

Response

{ "success": true, "messageId": "<unique-id@smtp.gmail.com>" }

Email Invoice

POST /billing/invoices/:id/send-email Auth Required

Send a formatted invoice email to the customer. Automatically sends a payment receipt when invoice status is "paid".

Request Body

FieldTypeRequiredDescription
reasonstringNo"payment_received" or "invoice_sent" (auto-detected from status)
POST /api/billing/invoices/abc123/send-email { "reason": "payment_received" }

Note: Invoice emails are also sent automatically when:

  • Invoice status is changed to "paid" via the dashboard
  • Razorpay webhook receives a payment.captured or payment_link.paid event
  • Razorpay checkout payment is verified via /razorpay/verify

Mail Configuration Status

GET /mail/status Auth Required

Check which SMTP provider is configured and active.

{ "configured": true, "provider": "gmail", "fromEmail": "akshaykotish@gmail.com", "fromName": "Akshay Kotish & Co.", "host": "smtp.gmail.com", "port": 587 }

Sent Emails History

GET /mail/sent Auth Required

Retrieve the last 50 sent emails with recipient, subject, type, and timestamp.

Query Parameters

ParamTypeDescription
limitnumberMax results (default: 50)

Email Aliases

GET /mail/aliases Auth Required

List all registered email aliases.

POST /mail/aliases Auth Required

Register a new email alias.

{ "alias": "hr@akshaykotish.com", "displayName": "HR Department", "employeeId": "optional-id" }
DELETE /mail/aliases/:id Auth Required

Delete an email alias by its Firestore document ID.

Health Check

GET /health Public
{ "status": "ok", "timestamp": "2026-03-16T00:00:00.000Z" }

Error Handling

All errors return a JSON object with an error field:

{ "error": "Description of what went wrong" }

HTTP Status Codes

CodeMeaning
200Success
201Resource created
400Bad request / validation error
401Authentication failed
403Insufficient permissions
404Resource not found
500Server error