Initial state for unpublished invoicesAn invoice in draft state has been created but not sent to the customer.Characteristics:
status: draft
sent_date: null
Customer has not been notified
No payment link active
Can be modified freely
Use Cases:
Review invoices before sending
Batch preparation of monthly invoices
Approval workflows
Price calculations that need verification
Example:
nodejs
Copy
const draft = await modempay.invoices.draft({ amount: 5000, customer_email: "customer@example.com", customer_name: "Adama Jobe", due_date: "2025-12-31"});console.log(draft.sent_date); // null// Customer has NOT received email// Can still modify or delete
Invoice successfully paidPayment has been received and confirmed.Characteristics:
status: "paid"
paid_date: <ISO timestamp>
Payment link no longer accepts payments
Final state (cannot transition back)
Triggers paymentrequest.success webhook event
Use Cases:
Successful subscription payment
Grant service access
Generate receipt
Update billing records
Provision resources
Example:
Copy
// Automatic when customer pays via link// Or manual for offline paymentsawait modempay.invoices.pay(invoice.id);// Retrieve to confirmconst paid = await modempay.invoices.retrieve(invoice.id);console.log(paid.status); // "paid"console.log(paid.paid_date); // "2025-11-24T14:22:00Z"
Webhook Handling:
Copy
// In your webhook endpointif (event.event === 'paymentrequest.success') { const invoice = event.payload; // Grant subscription access await activateSubscription(invoice.customer); // Send receipt await sendReceiptEmail(invoice); // Update internal records await recordPayment(invoice);}
Payment deadline passed without paymentThe due date has passed and the invoice remains unpaid (production mode only).Characteristics:
status: "overdue"
paid_date: null
Due date has passed
Only in production mode (not test mode)
Automatically set at midnight on due date
Triggers invoice.overdue webhook event
Can still transition to paid
Use Cases:
Subscription payment failures
Grace period enforcement
Service suspension
Collections process
Dunning management
Example:
Copy
// Automatic on due date in production// If invoice created on Nov 1 with due_date Nov 30// On Dec 1 at midnight: status becomes "overdue"// Handle overdue webhookif (event.event === 'invoice.overdue') { const invoice = event.payload; // Implement your policy await suspendSubscription(invoice.customer); await sendOverdueNotice(invoice); // Grace period: send reminders for 7 days await scheduleReminders(invoice, 7);}
Production Only: Overdue detection only works with live API keys (sk_live_). Test invoices never become overdue, allowing safe testing without time constraints.
// Not Paid → Paid (customer pays online)// Happens automatically via payment link// Or manually mark as paid (for offline payments)await modempay.invoices.pay(invoice.id);// Status: "paid", webhook triggered
// Not Paid → Overdue (automatic in production)// Happens at midnight on due_date if still unpaid// invoice.overdue webhook is sent// Overdue → Paid (customer finally pays)// Still possible to pay overdue invoices
async function handleInvoiceStatus(invoice: Invoice) { switch (invoice.status) { case "not-paid": // Check if reminder needed if (shouldSendReminder(invoice)) { await modempay.invoices.sendReminder(invoice.id); } break; case "paid": // Grant access await activateSubscription(invoice.customer); break; case "overdue": // Suspend service await suspendSubscription(invoice.customer); // Start dunning process await startDunningCampaign(invoice); break; }}
// Using test API key (sk_test_...)const testInvoice = await modempay.invoices.create({ amount: 5000, customer_email: "test@example.com", customer_name: "Adama Jobe", due_date: "2024-01-01" // Past date});console.log(testInvoice.test_mode); // trueconsole.log(testInvoice.status); // "not-paid"// Never becomes "overdue" even if due date passed
// Using live API key (sk_live_...)const liveInvoice = await modempay.invoices.create({ amount: 5000, customer_email: "customer@example.com", customer_name: "Adama Jobe", due_date: "2025-11-30"});console.log(liveInvoice.test_mode); // false// Will become "overdue" on Dec 1 if unpaid// invoice.overdue webhook will be sent
Don’t poll for status changes. Implement webhook handlers for paymentrequest.success and invoice.overdue events for real-time updates.
Set Reasonable Due Dates
For subscriptions, set due dates 7-14 days after invoice creation to give customers time to pay. For monthly billing, due date should be before the next billing cycle.
Implement Grace Periods for Overdue
Don’t immediately suspend services when invoices go overdue. Implement a 3-7 day grace period with automated reminders before taking action.
Use Drafts for Batch Processing
When generating invoices for many customers, create drafts first, review for accuracy, then publish in batches.
Track reminder_count
Monitor reminder_count to avoid spamming customers. Implement logic like: reminder at 7 days before due, at due date, and 3 days after due date.
Test Thoroughly in Test Mode
Test your entire flow in test mode before going live. Remember: test invoices never go overdue, so test overdue logic separately.