Skip to content

Invoice Management

Complete guide to managing invoices, viewing invoice details, and downloading PDF documents in the Reserva platform.


Overview

The invoice system provides comprehensive invoice management with support for:

  • Invoice Creation - Create invoices with automatic number generation and tax calculation
  • Invoice Listing - Paginated list of invoices with filtering
  • Invoice Details - Complete invoice information with line items
  • PDF Generation - Professional print-ready PDF invoices
  • Dual Authentication - Support for both staff and customer access
  • Multi-type Support - Subscription, appointment, and setup fee invoices
  • Payment Tracking - Real-time payment status and history

Key Concepts:

  • Staff Access = Full invoice management within tenant
  • Customer Access = View and download own invoices only
  • Invoice Types = SUBSCRIPTION, APPOINTMENT, SETUP_FEE
  • Invoice Status = DRAFT, SENT, PAID, OVERDUE, CANCELLED

Available Invoice Types

Subscription Invoices

  • Type: SUBSCRIPTION
  • Purpose: Subscription plan payments (upgrades, renewals)
  • Amount: Based on plan pricing and billing cycle
  • Related: Links to subscription record
  • Metadata: Includes plan details and billing period

Appointment Invoices

  • Type: APPOINTMENT
  • Purpose: Service booking payments
  • Amount: Service pricing + taxes
  • Related: Links to appointment record
  • Metadata: Includes service details and staff info

Setup Fee Invoices

  • Type: SETUP_FEE
  • Purpose: One-time tenant onboarding fees
  • Amount: Fixed setup amount
  • Related: Links to tenant record
  • Metadata: Includes setup package details

Create New Invoice

Create a new invoice with automatic number generation and tax calculations.

Endpoint

POST /api/v1/invoices

Authentication: Required (JWT token - Staff only)

Access Control

Staff Users:

  • Requires: TENANT_ADMIN or SUPER_ADMIN role
  • Can create invoices for their tenant only
  • All invoice data validated and calculated automatically

Permissions:

  • TENANT_ADMIN: Can create invoices within their tenant
  • SUPER_ADMIN: Can create invoices for any tenant

Request Body

{
  "tenant_id": "507f1f77bcf86cd799439011",
  "invoice_type": "appointment",
  "appointment_id": "507f1f77bcf86cd799439012",
  "customer_id": "507f1f77bcf86cd799439013",
  "bill_to_name": "John Doe",
  "bill_to_email": "john@example.com",
  "bill_to_phone": "+628123456789",
  "bill_to_address": "Jl. Sudirman No. 123, Jakarta",
  "line_items": [
    {
      "description": "Hair Cut & Styling",
      "quantity": 1,
      "unit_price": "150000.00",
      "amount": "150000.00",
      "tax_rate": 11.0,
      "tax_amount": "16500.00"
    }
  ],
  "notes": "Thank you for your business!",
  "currency": "IDR"
}

Required Fields:

Field Type Description Example
tenant_id string Tenant ID (must match authenticated user's tenant) 507f1f77bcf86cd799439011
invoice_type string Type: subscription, appointment, setup_fee appointment
bill_to_name string Customer or business name John Doe
bill_to_email string Email address for invoice delivery john@example.com
line_items array Array of line items (min 1 item) See example above

Optional Fields:

Field Type Description Example
customer_id string Customer ID (for appointment invoices) 507f1f77bcf86cd799439013
appointment_id string Appointment ID (for appointment invoices) 507f1f77bcf86cd799439012
subscription_id string Subscription ID (for subscription invoices) 507f1f77bcf86cd799439014
bill_to_phone string Customer phone number +628123456789
bill_to_address string Billing address Jl. Sudirman No. 123
notes string Additional notes or instructions Thank you!
currency string Currency code (default: IDR) IDR
invoice_date date Invoice creation date (default: today) 2025-01-15
due_date date Payment deadline (default: +30 days) 2025-02-14

Line Item Fields:

Field Type Required Description Example
description string Yes Item description Hair Cut & Styling
quantity number Yes Quantity (must be > 0) 1
unit_price decimal Yes Price per unit 150000.00
amount decimal Yes Total line amount (quantity × unit_price) 150000.00
tax_rate number No Tax percentage (default: 11.0 for Indonesia) 11.0
tax_amount decimal No Tax amount (amount × tax_rate / 100) 16500.00

Request Example

Create appointment invoice:

curl -X POST "https://api.myreserva.id/api/v1/invoices" \
  -H "Authorization: Bearer YOUR_STAFF_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "507f1f77bcf86cd799439011",
    "invoice_type": "appointment",
    "appointment_id": "507f1f77bcf86cd799439012",
    "customer_id": "507f1f77bcf86cd799439013",
    "bill_to_name": "John Doe",
    "bill_to_email": "john@example.com",
    "line_items": [
      {
        "description": "Hair Cut & Styling",
        "quantity": 1,
        "unit_price": "150000.00",
        "amount": "150000.00",
        "tax_rate": 11.0,
        "tax_amount": "16500.00"
      }
    ],
    "notes": "Thank you for your business!",
    "currency": "IDR"
  }'

Create subscription invoice:

curl -X POST "https://api.myreserva.id/api/v1/invoices" \
  -H "Authorization: Bearer YOUR_STAFF_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "507f1f77bcf86cd799439011",
    "invoice_type": "subscription",
    "subscription_id": "507f1f77bcf86cd799439014",
    "bill_to_name": "Salon Beauty Co.",
    "bill_to_email": "billing@salonbeauty.com",
    "line_items": [
      {
        "description": "PRO Plan - Monthly Subscription",
        "quantity": 1,
        "unit_price": "599000.00",
        "amount": "599000.00",
        "tax_rate": 11.0,
        "tax_amount": "65890.00"
      }
    ],
    "currency": "IDR"
  }'

Response

Success (201 Created):

{
  "id": "68ee2bb70db240b13fe242ab",
  "tenant_id": "507f1f77bcf86cd799439011",
  "invoice_number": "INV-2025-001",
  "invoice_type": "appointment",
  "invoice_date": "2025-01-15",
  "due_date": "2025-02-14",
  "status": "draft",
  "customer_id": "507f1f77bcf86cd799439013",
  "appointment_id": "507f1f77bcf86cd799439012",
  "bill_to_name": "John Doe",
  "bill_to_email": "john@example.com",
  "line_items": [
    {
      "description": "Hair Cut & Styling",
      "quantity": 1,
      "unit_price": "150000.00",
      "amount": "150000.00",
      "tax_rate": 11.0,
      "tax_amount": "16500.00"
    }
  ],
  "subtotal": "150000.00",
  "tax_total": "16500.00",
  "total_amount": "166500.00",
  "paid_amount": "0.00",
  "balance_due": "166500.00",
  "currency": "IDR",
  "notes": "Thank you for your business!",
  "created_at": "2025-01-15T10:30:00Z",
  "updated_at": "2025-01-15T10:30:00Z"
}

Automatic Calculations

The system automatically calculates the following:

  1. Invoice Number: Sequential per tenant (e.g., INV-2025-001, INV-2025-002)
  2. Subtotal: Sum of all line item amounts
  3. Tax Total: Sum of all line item tax amounts
  4. Total Amount: Subtotal + Tax Total
  5. Balance Due: Total Amount - Paid Amount (initially equals total)
  6. Due Date: Invoice date + 30 days (if not specified)
  7. Status: Initially set to DRAFT

Business Rules

  • Tenant Isolation: invoice.tenant_id must match authenticated user's tenant
  • Customer Validation: If customer_id provided, customer must exist in tenant
  • Appointment Validation: If appointment_id provided, appointment must exist in tenant
  • Subscription Validation: If subscription_id provided, subscription must exist in tenant
  • Sequential Numbers: Invoice numbers auto-generated per tenant (thread-safe)
  • Initial Status: All new invoices start with DRAFT status
  • Monetary Validation: All amounts validated and rounded to 2 decimal places
  • Line Items Required: Must have at least 1 line item
  • Tax Calculation: Tax amounts must match (amount × tax_rate / 100)

Error Responses

400 Bad Request - Validation error:

{
  "detail": "Line items are required"
}

403 Forbidden - Tenant mismatch:

{
  "detail": "Cannot create invoice for different tenant"
}

404 Not Found - Customer not found:

{
  "detail": "Customer not found"
}

422 Unprocessable Entity - Invalid data:

{
  "detail": [
    {
      "loc": ["body", "bill_to_email"],
      "msg": "value is not a valid email address",
      "type": "value_error.email"
    }
  ]
}

Integration with Other Systems

After creating an invoice, you can:

  1. Send to Customer: Use send invoice email endpoint (when implemented)
  2. Generate Payment Link: Create Paper.id payment request
  3. Download PDF: Use /invoices/{id}/download endpoint
  4. Update Status: Mark as sent, paid, or cancelled
  5. Track Payments: Monitor via webhooks and payment updates

List Invoices

Retrieve paginated list of invoices with comprehensive filtering options.

Endpoint

GET /api/v1/invoices

Authentication: Required (JWT token - Staff or Customer)

Query Parameters

Parameter Type Required Description Example
page integer No Page number (1-based, default: 1) 1
size integer No Items per page (1-100, default: 20) 20
status_filter string No Filter by status paid
invoice_type string No Filter by type appointment
start_date date No Start date (YYYY-MM-DD) 2025-01-01
end_date date No End date (YYYY-MM-DD) 2025-12-31
customer_id string No Filter by customer (staff only) 507f1f77bcf86cd799439011

Status Values: draft, sent, paid, overdue, cancelled

Invoice Types: subscription, appointment, setup_fee

Access Control

Staff Users:

  • Can view all invoices within their tenant
  • Can use customer_id filter to find specific customer invoices
  • Requires: STAFF role or higher
  • Tenant isolation automatically enforced

Customer Users:

  • Can only view their own invoices
  • customer_id parameter ignored (automatically filtered)
  • Requires: Valid customer JWT token
  • Access restricted to invoices where they are bill_to

Super Admin:

  • Can view invoices across all tenants
  • Requires: SUPER_ADMIN role

Request Example

Staff - List all invoices:

curl -X GET "https://api.myreserva.id/api/v1/invoices?page=1&size=20&status_filter=paid" \
  -H "Authorization: Bearer YOUR_STAFF_JWT_TOKEN"

Staff - Filter by customer:

curl -X GET "https://api.myreserva.id/api/v1/invoices?customer_id=507f1f77bcf86cd799439011" \
  -H "Authorization: Bearer YOUR_STAFF_JWT_TOKEN"

Customer - List own invoices:

curl -X GET "https://api.myreserva.id/api/v1/invoices?page=1&size=10" \
  -H "Authorization: Bearer YOUR_CUSTOMER_JWT_TOKEN"

Response

{
  "items": [
    {
      "id": "68ee2bb70db240b13fe242ab",
      "invoice_number": "INV-2025-001",
      "invoice_type": "appointment",
      "bill_to_name": "John Doe",
      "total_amount": "166500.00",
      "paid_amount": "166500.00",
      "currency": "IDR",
      "status": "paid",
      "invoice_date": "2025-01-15",
      "due_date": "2025-02-14",
      "created_at": "2025-01-15T10:30:00Z"
    },
    {
      "id": "68ee2bb70db240b13fe242ac",
      "invoice_number": "INV-2025-002",
      "invoice_type": "subscription",
      "bill_to_name": "Salon Beauty Co.",
      "total_amount": "599000.00",
      "paid_amount": "0.00",
      "currency": "IDR",
      "status": "sent",
      "invoice_date": "2025-01-20",
      "due_date": "2025-02-19",
      "created_at": "2025-01-20T14:20:00Z"
    }
  ],
  "total": 45,
  "page": 1,
  "size": 20,
  "pages": 3
}

Field Descriptions:

  • items - Array of invoice summary objects
  • total - Total count of invoices matching filter
  • page - Current page number
  • size - Items per page
  • pages - Total number of pages
  • invoice_number - Unique sequential invoice number per tenant
  • invoice_type - Type of invoice (subscription/appointment/setup_fee)
  • bill_to_name - Customer or business name
  • total_amount - Total invoice amount including taxes
  • paid_amount - Amount paid so far (for partial payments)
  • status - Current invoice status
  • invoice_date - Date invoice was created
  • due_date - Payment due date

Business Rules

  • Tenant Isolation: Automatically filtered by authenticated user's tenant
  • Customer Filtering: Customers see only their own invoices (customer_id auto-applied)
  • Staff Filtering: Staff can filter by any customer in their tenant
  • Soft Deletion: Deleted invoices excluded from results
  • Sorting: Invoices sorted by creation date (newest first)
  • Pagination: Maximum 100 items per page

Subscription Plan Limitations

Invoice access is not directly limited by subscription plan, but invoice creation follows these rules:

Plan Max Appointments/Month Impact on Appointment Invoices
FREE 100 Limited appointment invoices
PRO 2,000 Standard invoice volume
ENTERPRISE Unlimited Unlimited invoices

Note: Subscription invoices (for plan payments) are not subject to plan limits.


Get Invoice Details

Retrieve detailed information about a specific invoice including all line items and payment history.

Endpoint

GET /api/v1/invoices/{invoice_id}

Authentication: Required (JWT token - Staff or Customer)

Path Parameters

Parameter Type Required Description Example
invoice_id string Yes Invoice ID (ObjectId) 68ee2bb70db240b13fe242ab

Access Control

Staff Users:

  • Can view any invoice within their tenant
  • Full access to all invoice details
  • Requires: STAFF role or higher

Customer Users:

  • Can only view their own invoices
  • Returns 403 Forbidden if accessing another customer's invoice
  • Requires: Valid customer JWT token

Super Admin:

  • Can view invoices across all tenants
  • Requires: SUPER_ADMIN role

Request Example

Staff:

curl -X GET "https://api.myreserva.id/api/v1/invoices/68ee2bb70db240b13fe242ab" \
  -H "Authorization: Bearer YOUR_STAFF_JWT_TOKEN"

Customer:

curl -X GET "https://api.myreserva.id/api/v1/invoices/68ee2bb70db240b13fe242ab" \
  -H "Authorization: Bearer YOUR_CUSTOMER_JWT_TOKEN"

Response

{
  "id": "68ee2bb70db240b13fe242ab",
  "tenant_id": "507f1f77bcf86cd799439010",
  "invoice_number": "INV-2025-001",
  "invoice_type": "appointment",
  "invoice_date": "2025-01-15",
  "due_date": "2025-02-14",
  "status": "paid",
  "customer_id": "507f1f77bcf86cd799439013",
  "appointment_id": "507f1f77bcf86cd799439012",
  "bill_to_name": "John Doe",
  "bill_to_email": "john@example.com",
  "bill_to_phone": "+628123456789",
  "bill_to_address": "Jl. Sudirman No. 123, Jakarta",
  "line_items": [
    {
      "description": "Hair Cut & Styling",
      "quantity": 1,
      "unit_price": "150000.00",
      "amount": "150000.00",
      "tax_rate": 11.0,
      "tax_amount": "16500.00"
    }
  ],
  "subtotal": "150000.00",
  "tax_total": "16500.00",
  "total_amount": "166500.00",
  "paid_amount": "166500.00",
  "balance_due": "0.00",
  "currency": "IDR",
  "notes": "Thank you for your business!",
  "payment_method": "paper_id",
  "paper_invoice_id": "PI-20250115-ABC123",
  "paper_invoice_url": "https://stg-v2.paper.id/abc123",
  "paper_payment_url": "https://payper.id/short123",
  "paid_at": "2025-01-15T11:00:00Z",
  "created_at": "2025-01-15T10:30:00Z",
  "updated_at": "2025-01-15T11:00:00Z"
}

Field Descriptions:

  • id - Unique invoice identifier
  • tenant_id - Tenant this invoice belongs to
  • invoice_number - Human-readable sequential invoice number
  • invoice_type - Type: subscription, appointment, or setup_fee
  • invoice_date - Date invoice was created
  • due_date - Payment deadline
  • status - Current status (draft/sent/paid/overdue/cancelled)
  • customer_id - Customer this invoice is billed to (optional)
  • appointment_id - Related appointment ID (for appointment invoices)
  • bill_to_* - Billing contact information
  • line_items[] - Array of invoice line items with pricing
  • subtotal - Sum of all line item amounts before tax
  • tax_total - Sum of all tax amounts
  • total_amount - Grand total including all taxes
  • paid_amount - Amount received so far
  • balance_due - Remaining amount owed (total - paid)
  • currency - Currency code (IDR, USD, etc.)
  • notes - Additional notes or instructions
  • payment_method - Payment gateway used (paper_id)
  • paper_* - Paper.id integration fields for payment
  • paid_at - Timestamp when full payment received

Invoice Status Lifecycle

DRAFT → SENT → PAID
  ↓       ↓
CANCELLED OVERDUE

Status Descriptions:

  • DRAFT - Invoice created but not sent to customer
  • SENT - Invoice delivered to customer, awaiting payment
  • PAID - Full payment received, invoice closed
  • OVERDUE - Past due date without payment (auto-updated)
  • CANCELLED - Invoice voided or cancelled

Error Responses

400 Bad Request - Invalid invoice ID format:

{
  "detail": "Invalid invoice ID format"
}

403 Forbidden - Customer accessing wrong invoice:

{
  "detail": "You can only access your own invoices"
}

404 Not Found - Invoice doesn't exist:

{
  "detail": "Invoice not found"
}

Business Rules

  • Tenant Isolation: Invoice must belong to authenticated user's tenant
  • Customer Restriction: Customers can only access invoices where they are the bill_to
  • Staff Access: Staff have full read access within their tenant
  • Soft Deletion: Deleted invoices return 404
  • ObjectId Validation: invoice_id must be valid MongoDB ObjectId (24 hex chars)

Download Invoice PDF

Download invoice as professionally formatted PDF document for printing or record-keeping.

Endpoint

GET /api/v1/invoices/{invoice_id}/download

Authentication: Required (JWT token - Staff or Customer)

Path Parameters

Parameter Type Required Description Example
invoice_id string Yes Invoice ID (ObjectId) 68ee2bb70db240b13fe242ab

Access Control

Staff Users:

  • Can download any invoice within their tenant
  • Full access to PDF generation
  • Requires: STAFF role or higher

Customer Users:

  • Can only download their own invoices
  • Returns 403 Forbidden if accessing another customer's invoice
  • Requires: Valid customer JWT token

Super Admin:

  • Can download invoices across all tenants
  • Requires: SUPER_ADMIN role

PDF Features

The generated PDF includes:

  • Professional A4 Layout - Print-ready formatting
  • Tenant Branding - Company name, email, phone, website
  • Invoice Metadata - Invoice number, dates, status
  • Line Items Table - Detailed breakdown with quantities and pricing
  • Tax Calculations - Per-line tax amounts (PPN 11% for Indonesia)
  • Totals Section - Subtotal, tax total, grand total
  • Payment Status - Paid amount and balance due
  • Custom Notes - Additional information from invoice
  • Generation Timestamp - PDF creation date footer

Request Example

Staff - Download any invoice:

curl -X GET "https://api.myreserva.id/api/v1/invoices/68ee2bb70db240b13fe242ab/download" \
  -H "Authorization: Bearer YOUR_STAFF_JWT_TOKEN" \
  --output invoice.pdf

Customer - Download own invoice:

curl -X GET "https://api.myreserva.id/api/v1/invoices/68ee2bb70db240b13fe242ab/download" \
  -H "Authorization: Bearer YOUR_CUSTOMER_JWT_TOKEN" \
  --output invoice.pdf

JavaScript/Frontend Example:

async function downloadInvoice(invoiceId, authToken) {
  const response = await fetch(
    `https://api.myreserva.id/api/v1/invoices/${invoiceId}/download`,
    {
      headers: {
        'Authorization': `Bearer ${authToken}`
      }
    }
  );

  if (!response.ok) throw new Error('Download failed');

  // Get filename from Content-Disposition header
  const contentDisposition = response.headers.get('Content-Disposition');
  const filename = contentDisposition
    ? contentDisposition.split('filename=')[1].replace(/"/g, '')
    : `invoice_${invoiceId}.pdf`;

  // Create blob and trigger download
  const blob = await response.blob();
  const url = window.URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  window.URL.revokeObjectURL(url);
  document.body.removeChild(a);
}

Response

Success (200 OK):

  • Content-Type: application/pdf
  • Content-Disposition: attachment; filename="invoice_INV-2025-001.pdf"
  • Body: Binary PDF file stream

Typical File Size: 50-200KB per invoice

Frontend Integration

For comprehensive frontend integration examples including React, Vue, and error handling, see:

📖 Frontend Integration Guide - Complete implementation examples for all major frameworks

Key Integration Topics Covered:

  • Basic download with vanilla JavaScript
  • Preview in new tab
  • React/TypeScript component example
  • Axios implementation
  • Vue 3 Composition API
  • Common pitfalls and solutions
  • UX best practices
  • Testing checklist

Error Responses

400 Bad Request - Invalid invoice ID:

{
  "detail": "Invalid invoice ID format"
}

401 Unauthorized - Missing or invalid token:

{
  "detail": "Not authenticated"
}

403 Forbidden - Customer accessing wrong invoice:

{
  "detail": "You can only download your own invoices"
}

404 Not Found - Invoice doesn't exist:

{
  "detail": "Invoice not found"
}

500 Internal Server Error - PDF generation failed:

{
  "detail": "Failed to generate PDF: TenantInDB object has no attribute 'name'"
}

Performance Considerations

Metric Value Notes
Generation Time 100-300ms Warm server
Cold Start 1-2 seconds First request (serverless)
File Size 50-200KB Depends on line items
Concurrent Downloads Unlimited No performance degradation

Optimization Tips:

  • Show loading spinner during generation
  • Cache invoice data (not PDF) on frontend
  • Use Content-Disposition filename for better UX
  • Clean up blob URLs to prevent memory leaks

Business Rules

  • On-Demand Generation: PDFs generated fresh on each request (not cached)
  • Tenant Isolation: Invoice must belong to authenticated user's tenant
  • Customer Restriction: Customers can only download their own invoices
  • Staff Access: Staff can download any invoice in their tenant
  • Format Validation: invoice_id must be valid ObjectId
  • Filename Format: invoice_{invoice_number}.pdf

UX Best Practices

  1. Loading State: Show "Generating PDF..." during request
  2. Success Feedback: Display filename after successful download
  3. Error Handling: Show user-friendly error messages
  4. Mobile Support: PDFs may preview instead of download on mobile (browser behavior)
  5. Download History: Track downloads for user convenience
  6. Dual Actions: Provide both "Download" and "Preview" buttons

Common Use Cases

1. Customer Invoice Portal

Scenario: Customer wants to view and download their invoices

# Step 1: List customer's invoices
curl -X GET "https://api.myreserva.id/api/v1/invoices?page=1&size=10" \
  -H "Authorization: Bearer CUSTOMER_JWT_TOKEN"

# Step 2: Get detailed invoice information
curl -X GET "https://api.myreserva.id/api/v1/invoices/68ee2bb70db240b13fe242ab" \
  -H "Authorization: Bearer CUSTOMER_JWT_TOKEN"

# Step 3: Download PDF for records
curl -X GET "https://api.myreserva.id/api/v1/invoices/68ee2bb70db240b13fe242ab/download" \
  -H "Authorization: Bearer CUSTOMER_JWT_TOKEN" \
  --output invoice.pdf

2. Staff Invoice Management

Scenario: Staff needs to review unpaid invoices for follow-up

# Filter by status and date range
curl -X GET "https://api.myreserva.id/api/v1/invoices?status_filter=sent&start_date=2025-01-01&end_date=2025-01-31" \
  -H "Authorization: Bearer STAFF_JWT_TOKEN"

# Get details for specific invoice
curl -X GET "https://api.myreserva.id/api/v1/invoices/68ee2bb70db240b13fe242ac" \
  -H "Authorization: Bearer STAFF_JWT_TOKEN"

3. Accounting Export

Scenario: Accountant needs all paid invoices for reporting

# Get all paid invoices for the month
curl -X GET "https://api.myreserva.id/api/v1/invoices?status_filter=paid&start_date=2025-01-01&end_date=2025-01-31&size=100" \
  -H "Authorization: Bearer STAFF_JWT_TOKEN"

API Reference Summary

Endpoint Method Purpose Key Response Access
/invoices POST Create new invoice Complete invoice with generated number TENANT_ADMIN+
/invoices GET List invoices with filtering Paginated invoice summaries Staff/Customer
/invoices/{id} GET Get invoice details Complete invoice with line items Staff/Customer
/invoices/{id}/download GET Download PDF Binary PDF file stream Staff/Customer


Troubleshooting

Invoice Not Found (404)

Symptoms: Getting 404 when accessing invoice

Checks:

  1. Verify invoice ID is valid ObjectId (24 hex characters)
  2. Check invoice hasn't been soft-deleted
  3. Confirm invoice belongs to your tenant
  4. Ensure you're using correct authentication token

Fix:

  • Use exact invoice ID from list endpoint
  • Check invoice status is not deleted
  • Verify tenant context

PDF Download Corrupted

Symptoms: Downloaded PDF won't open or displays garbled text

Checks:

  1. Verify using responseType: 'blob' in Axios/fetch
  2. Check Content-Type header is application/pdf
  3. Ensure no text processing on response body
  4. Verify complete file download (check file size)

Fix:

// CORRECT - Use blob response type
const response = await axios.get(url, {
  responseType: 'blob'
});

Customer Can't Access Invoice (403)

Symptoms: Customer gets 403 Forbidden error

Checks:

  1. Verify customer is accessing their own invoice
  2. Check invoice customer_id matches authenticated customer
  3. Confirm customer token is valid and not expired

Fix:

  • Ensure customer_id in invoice matches authenticated customer
  • Use correct customer JWT token

PDF Generation Failed (500)

Symptoms: Server error during PDF generation

Checks:

  1. Check invoice data is complete (has line items)
  2. Verify tenant information exists in database
  3. Review server logs for specific error
  4. Ensure all required fields present

Fix:

  • Contact backend support with invoice ID
  • Provide error message from response
  • Check invoice data completeness

Best Practices

For Invoice Listing

DO:

  • Use pagination for large result sets
  • Filter by status to find specific invoices
  • Sort by date for chronological view
  • Cache results briefly on frontend

DON'T:

  • Request size > 100 items per page
  • Ignore pagination metadata
  • Skip error handling
  • Assume unlimited results

For Invoice Details

DO:

  • Validate invoice ID format before request
  • Handle 403 errors gracefully for customers
  • Display all line items clearly
  • Show payment status prominently

DON'T:

  • Hardcode invoice IDs
  • Ignore access control errors
  • Skip validation
  • Assume invoice exists

For PDF Downloads

DO:

  • Show loading state during generation
  • Use blob response type for binary data
  • Clean up blob URLs after use
  • Handle mobile preview behavior
  • Provide both download and preview options

DON'T:

  • Process PDF as text
  • Skip Content-Disposition filename
  • Create memory leaks with unreleased blobs
  • Ignore error responses
  • Assume instant generation

Next Steps:

  1. Create your first invoice: POST /invoices
  2. Review invoice listing: GET /invoices
  3. Check invoice details: GET /invoices/{id}
  4. Test PDF download: GET /invoices/{id}/download
  5. Implement frontend integration using invoice-download.md
  6. Set up error handling for all endpoints