Skip to content

Invoice Workflow

Overview

Invoice workflow manages the lifecycle of invoices from creation through payment collection. This guide covers invoice approval, sending to customers, payment tracking, overdue management, and automated reminder systems.

Table of Contents

Invoice Approval

Approving an Invoice

Endpoint: POST /api/v1/sales/invoices/{invoice}/approve

Authentication: Required (Bearer token)

Purpose: Approve invoice for sending (required for high-value invoices or specific workflows)

Prerequisites:

  • Invoice must be in draft or pending_approval status
  • User must have approval permissions
  • Invoice details must be accurate and complete

Request Body:

json
{
  "approval_notes": "Reviewed and approved for sending to customer",
  "approved_by": 5
}

Response (200 OK):

json
{
  "message": "Invoice approved successfully",
  "data": {
    "id": 456,
    "invoice_number": "INV-20251217-001",
    "status": "approved",
    "approved_at": "2025-12-17T13:00:00Z",
    "approved_by": {
      "id": 5,
      "name": "Jane Manager",
      "email": "jane.manager@company.com"
    }
  }
}

Approval Triggers:

  • Invoice total exceeds threshold (e.g., > $10,000)
  • Custom pricing or discounts applied
  • Credit terms extended
  • Customer has outstanding overdue invoices
  • Manual approval workflow enabled

Error Response:

json
{
  "error": "Cannot approve invoice",
  "message": "Invoice must be in draft or pending_approval status. Current status: sent"
}

Sending Invoices

Send Invoice to Customer

Endpoint: POST /api/v1/sales/invoices/{invoice}/send

Authentication: Required (Bearer token)

Purpose: Send invoice to customer via email with PDF attachment

Prerequisites:

  • Invoice must be in draft or approved status
  • Customer must have valid email address
  • Invoice must be complete and accurate

Request Body:

json
{
  "email_to": ["billing@acme.com"],
  "email_cc": ["manager@acme.com"],
  "email_bcc": ["accounting@company.com"],
  "subject": "Invoice INV-20251217-001 from Your Company",
  "message": "Dear Customer,\n\nPlease find attached invoice INV-20251217-001 for your recent order.\n\nPayment is due within 30 days. If you have any questions, please don't hesitate to contact us.\n\nThank you for your business!",
  "attach_pdf": true,
  "send_copy_to_self": true
}

Response (200 OK):

json
{
  "message": "Invoice sent successfully",
  "data": {
    "id": 456,
    "invoice_number": "INV-20251217-001",
    "status": "sent",
    "sent_date": "2025-12-17T14:00:00Z",
    "sent_by": {
      "id": 3,
      "name": "John Sales",
      "email": "john.sales@company.com"
    },
    "email_details": {
      "sent_to": ["billing@acme.com"],
      "sent_cc": ["manager@acme.com"],
      "sent_bcc": ["accounting@company.com"],
      "pdf_attached": true,
      "delivery_status": "sent"
    }
  }
}

What Happens When Sending:

  1. Invoice status changes to sent
  2. sent_date timestamp recorded
  3. PDF invoice generated automatically
  4. Email sent with PDF attachment
  5. Customer portal link included (if enabled)
  6. Invoice appears in accounts receivable
  7. Due date tracking begins

Generate PDF Without Sending

Endpoint: GET /api/v1/sales/invoices/{invoice}/pdf

Authentication: Required (Bearer token)

Response: Binary PDF file download

Use Case: Preview invoice before sending, save for records, manual delivery

Customizing Email Template

json
{
  "email_to": ["billing@acme.com"],
  "subject": "[Action Required] Invoice {{invoice_number}} - Due {{due_date}}",
  "message": "Hello {{customer_name}},\n\nInvoice {{invoice_number}} for {{total_amount}} is now available.\n\nPayment Terms: {{payment_terms}}\nDue Date: {{due_date}}\n\nView online: {{portal_link}}\n\nThank you!",
  "template_variables": {
    "custom_field_1": "Value 1",
    "custom_field_2": "Value 2"
  }
}

Available Variables:

  • Custom variables from request

Payment Tracking

Applying Payment to Invoice

Endpoint: POST /api/v1/sales/invoices/{invoice}/apply-payment

Authentication: Required (Bearer token)

Purpose: Record payment received against invoice

Request Body (Full Payment):

json
{
  "payment_id": 789,
  "amount": 347.47,
  "payment_date": "2025-12-18",
  "payment_method": "bank_transfer",
  "reference_number": "WIRE-123456",
  "notes": "Payment received via wire transfer"
}

Response (200 OK):

json
{
  "message": "Payment applied successfully",
  "data": {
    "id": 456,
    "invoice_number": "INV-20251217-001",
    "status": "paid",
    "total_amount": 347.47,
    "amount_paid": 347.47,
    "amount_due": 0.00,
    "paid_date": "2025-12-18T10:00:00Z",
    "payment": {
      "id": 789,
      "payment_number": "PAY-12345678",
      "amount": 347.47,
      "method": "bank_transfer"
    }
  }
}

Partial Payment

Request Body:

json
{
  "amount": 100.00,
  "payment_date": "2025-12-18",
  "payment_method": "credit_card",
  "notes": "Partial payment - balance to follow"
}

Response:

json
{
  "data": {
    "id": 456,
    "invoice_number": "INV-20251217-001",
    "status": "partially_paid",
    "total_amount": 347.47,
    "amount_paid": 100.00,
    "amount_due": 247.47,
    "payment_percentage": 28.77
  }
}

Viewing Invoice Payments

Endpoint: GET /api/v1/sales/invoices/{invoice}/payments

Authentication: Required (Bearer token)

Response (200 OK):

json
{
  "data": [
    {
      "id": 789,
      "payment_number": "PAY-12345678",
      "status": "completed",
      "amount": 100.00,
      "method": "credit_card",
      "payment_date": "2025-12-18T10:00:00Z",
      "transaction_id": "TXN-987654"
    },
    {
      "id": 790,
      "payment_number": "PAY-12345679",
      "status": "completed",
      "amount": 247.47,
      "method": "bank_transfer",
      "payment_date": "2025-12-22T14:30:00Z",
      "transaction_id": "WIRE-789456"
    }
  ],
  "meta": {
    "total_payments": 2,
    "total_paid": 347.47,
    "payment_complete": true
  }
}

Marking Invoices as Paid

Mark as Paid Manually

Endpoint: POST /api/v1/sales/invoices/{invoice}/mark-paid

Authentication: Required (Bearer token)

Purpose: Manually mark invoice as paid (when payment received outside system)

Request Body:

json
{
  "paid_date": "2025-12-18",
  "payment_method": "check",
  "reference_number": "CHECK-789456",
  "notes": "Payment received via check - deposited 12/18/2025"
}

Response (200 OK):

json
{
  "message": "Invoice marked as paid successfully",
  "data": {
    "id": 456,
    "invoice_number": "INV-20251217-001",
    "status": "paid",
    "total_amount": 347.47,
    "amount_paid": 347.47,
    "amount_due": 0.00,
    "paid_date": "2025-12-18T00:00:00Z"
  }
}

Use Cases:

  • Cash payments
  • Check payments
  • Wire transfers verified via bank statement
  • Third-party payment processors
  • Payments received before invoice sent

Canceling Invoices

Cancel Invoice

Endpoint: POST /api/v1/sales/invoices/{invoice}/cancel

Authentication: Required (Bearer token)

Purpose: Cancel invoice (void for non-payment, corrections, or errors)

Prerequisites:

  • Invoice must NOT be paid
  • Invoice can be in any status except paid
  • Cancellation reason required for audit trail

Request Body:

json
{
  "cancellation_reason": "Invoice issued in error - duplicate billing",
  "notify_customer": true,
  "refund_payments": false
}

Response (200 OK):

json
{
  "message": "Invoice cancelled successfully",
  "data": {
    "id": 456,
    "invoice_number": "INV-20251217-001",
    "status": "cancelled",
    "cancelled_at": "2025-12-17T16:00:00Z",
    "cancellation_reason": "Invoice issued in error - duplicate billing",
    "customer_notified": true
  }
}

What Happens on Cancellation:

  1. Invoice status changes to cancelled
  2. cancelled_at timestamp and reason recorded
  3. Removed from accounts receivable
  4. Customer notified if requested
  5. Partial payments refunded if specified
  6. Cannot be reactivated (create new invoice instead)

Cancel with Refund

For invoices with partial payments:

json
{
  "cancellation_reason": "Order cancelled - product unavailable",
  "notify_customer": true,
  "refund_payments": true,
  "refund_method": "original_payment_method"
}

Response includes:

json
{
  "data": {
    "status": "cancelled",
    "refund_initiated": true,
    "refund_amount": 100.00,
    "refund_status": "processing"
  }
}

Overdue Invoice Management

Get Overdue Invoices

Endpoint: GET /api/v1/sales/invoices/overdue

Authentication: Required (Bearer token)

Purpose: List all invoices past due date without full payment

Query Parameters:

  • days_overdue (integer) - Minimum days overdue (default: 1)
  • customer_id (integer) - Filter by customer
  • min_amount (number) - Minimum invoice amount
  • sort_by (string) - Sort field (due_date, days_overdue, amount_due)
  • sort_direction (string) - Sort direction (asc, desc)

Response (200 OK):

json
{
  "data": [
    {
      "id": 458,
      "invoice_number": "INV-20251115-003",
      "status": "overdue",
      "customer": {
        "id": 45,
        "customer_number": "CUST-00045",
        "company_name": "Late Payer Inc"
      },
      "total_amount": 1250.00,
      "amount_paid": 0.00,
      "amount_due": 1250.00,
      "issue_date": "2025-11-15",
      "due_date": "2025-12-15",
      "days_overdue": 2,
      "late_fee_amount": 25.00
    },
    {
      "id": 459,
      "invoice_number": "INV-20251110-004",
      "status": "overdue",
      "customer": {
        "id": 46,
        "customer_number": "CUST-00046",
        "company_name": "Slow Pay LLC"
      },
      "total_amount": 875.50,
      "amount_paid": 200.00,
      "amount_due": 675.50,
      "issue_date": "2025-11-10",
      "due_date": "2025-12-10",
      "days_overdue": 7,
      "late_fee_amount": 33.78
    }
  ],
  "meta": {
    "total_overdue": 2,
    "total_overdue_amount": 1925.50,
    "average_days_overdue": 4.5
  }
}

Automatic Overdue Detection

The system automatically:

  • Checks invoice due dates daily
  • Updates status to overdue when due_date passes
  • Sets overdue_date timestamp
  • Applies late fees if configured
  • Triggers reminder workflows

Invoice Reminders

Send Payment Reminders

Endpoint: POST /api/v1/sales/invoices/send-reminders

Authentication: Required (Bearer token)

Purpose: Send automated payment reminders for overdue or due-soon invoices

Request Body:

json
{
  "reminder_type": "overdue",
  "days_overdue_min": 1,
  "days_overdue_max": 30,
  "exclude_customers": [45],
  "email_template": "overdue_payment_reminder",
  "dry_run": false
}

Reminder Types:

  • due_soon - Invoices due within N days
  • due_today - Invoices due today
  • overdue - Invoices past due date
  • seriously_overdue - Invoices 30+ days overdue

Response (200 OK):

json
{
  "message": "Payment reminders sent successfully",
  "data": {
    "reminder_type": "overdue",
    "invoices_processed": 15,
    "reminders_sent": 12,
    "reminders_failed": 3,
    "results": [
      {
        "invoice_id": 458,
        "invoice_number": "INV-20251115-003",
        "customer_email": "billing@latepayer.com",
        "status": "sent",
        "sent_at": "2025-12-17T17:00:00Z"
      },
      {
        "invoice_id": 459,
        "invoice_number": "INV-20251110-004",
        "customer_email": "ap@slowpay.com",
        "status": "sent",
        "sent_at": "2025-12-17T17:00:01Z"
      }
    ]
  }
}

Reminder Schedule

Typical reminder schedule:

  1. 7 days before due: Friendly reminder
  2. Due date: Payment due today notice
  3. 3 days overdue: First overdue notice
  4. 7 days overdue: Second overdue notice
  5. 14 days overdue: Final notice
  6. 30 days overdue: Collection warning

Custom Reminder Template

json
{
  "reminder_type": "overdue",
  "email_subject": "URGENT: Invoice {{invoice_number}} is {{days_overdue}} days overdue",
  "email_message": "Dear {{customer_name}},\n\nInvoice {{invoice_number}} for {{total_amount}} is now {{days_overdue}} days overdue.\n\nOriginal Due Date: {{due_date}}\nAmount Due: {{amount_due}}\n\nPlease remit payment immediately to avoid late fees and collection action.\n\nLate Fee: {{late_fee_amount}}\nTotal Now Due: {{total_with_late_fee}}\n\nThank you.",
  "days_overdue_min": 7
}

Invoice Aging Report

Get Aging Report

Endpoint: GET /api/v1/sales/invoices/aging-report

Authentication: Required (Bearer token)

Purpose: View accounts receivable aging analysis

Query Parameters:

  • as_of_date (date) - Report date (default: today)
  • customer_id (integer) - Filter by customer
  • include_paid (boolean) - Include paid invoices (default: false)

Response (200 OK):

json
{
  "data": {
    "report_date": "2025-12-17",
    "aging_buckets": {
      "current": {
        "label": "Current (Not Due)",
        "count": 45,
        "total_amount": 125678.90
      },
      "1_30_days": {
        "label": "1-30 Days Overdue",
        "count": 12,
        "total_amount": 34567.89
      },
      "31_60_days": {
        "label": "31-60 Days Overdue",
        "count": 5,
        "total_amount": 12345.67
      },
      "61_90_days": {
        "label": "61-90 Days Overdue",
        "count": 2,
        "total_amount": 5678.90
      },
      "over_90_days": {
        "label": "Over 90 Days",
        "count": 1,
        "total_amount": 3456.78
      }
    },
    "summary": {
      "total_invoices": 65,
      "total_outstanding": 181728.14,
      "overdue_percentage": 30.8,
      "average_days_to_pay": 25.3
    },
    "by_customer": [
      {
        "customer_id": 42,
        "customer_name": "Acme Corporation",
        "current": 15000.00,
        "1_30_days": 5000.00,
        "31_60_days": 0.00,
        "61_90_days": 0.00,
        "over_90_days": 0.00,
        "total_outstanding": 20000.00
      }
    ]
  }
}

Exporting Aging Report

Endpoint: GET /api/v1/sales/invoices/aging-report?format=csv

Response: CSV file download with aging details

Invoice Statistics

Get Invoice Statistics

Endpoint: GET /api/v1/sales/invoices/statistics

Authentication: Required (Bearer token)

Query Parameters:

  • from_date (date) - Start date
  • to_date (date) - End date
  • customer_id (integer) - Filter by customer

Response (200 OK):

json
{
  "data": {
    "period": {
      "from": "2025-12-01",
      "to": "2025-12-31"
    },
    "invoice_counts": {
      "total": 125,
      "draft": 8,
      "sent": 35,
      "partially_paid": 12,
      "paid": 65,
      "overdue": 4,
      "cancelled": 1
    },
    "financial_metrics": {
      "total_invoiced": 456789.12,
      "total_paid": 398765.43,
      "total_outstanding": 58023.69,
      "average_invoice_value": 3654.31,
      "collection_rate": 87.29
    },
    "payment_metrics": {
      "average_days_to_pay": 18.5,
      "on_time_payment_rate": 82.5,
      "late_payment_rate": 17.5,
      "total_late_fees": 1234.56
    },
    "top_customers_by_revenue": [
      {
        "customer_id": 42,
        "customer_name": "Acme Corporation",
        "invoice_count": 15,
        "total_invoiced": 125678.90,
        "total_paid": 125678.90,
        "payment_performance": "excellent"
      }
    ]
  }
}

Invoice Activities

View Invoice Activity History

Endpoint: GET /api/v1/sales/invoices/{invoice}/activities

Authentication: Required (Bearer token)

Response (200 OK):

json
{
  "data": [
    {
      "id": 1234,
      "type": "invoice_paid",
      "description": "Invoice paid in full",
      "user": {
        "id": null,
        "name": "System"
      },
      "metadata": {
        "payment_id": 789,
        "payment_method": "bank_transfer",
        "amount": 347.47
      },
      "created_at": "2025-12-18T10:00:00Z"
    },
    {
      "id": 1233,
      "type": "invoice_sent",
      "description": "Invoice sent to customer",
      "user": {
        "id": 3,
        "name": "John Sales"
      },
      "metadata": {
        "sent_to": ["billing@acme.com"],
        "pdf_attached": true
      },
      "created_at": "2025-12-17T14:00:00Z"
    },
    {
      "id": 1232,
      "type": "invoice_created",
      "description": "Invoice created from order ORD-A3X7K9M2",
      "user": {
        "id": 5,
        "name": "Jane Manager"
      },
      "metadata": {
        "order_id": 123,
        "total_amount": 347.47
      },
      "created_at": "2025-12-17T11:30:00Z"
    }
  ]
}

Best Practices

  1. Approve Before Sending: Review invoices for accuracy before sending to customers

  2. Send Promptly: Send invoices immediately after service delivery or product shipment

  3. Use Clear Payment Terms: Specify payment methods, due dates, and late fee policies

  4. Track Partial Payments: Record all payments promptly to maintain accurate balances

  5. Follow Up on Overdue: Send reminders at 3, 7, and 14 days overdue

  6. Document Cancellations: Always provide detailed reasons for cancelled invoices

  7. Monitor Aging Report: Review weekly to identify collection issues early

  8. Automate Reminders: Set up automatic reminder schedules to reduce manual work

Documentation for SynthesQ CRM/ERP Platform